离开 Activity 时 Android 如何处理后台线程?

Posted

技术标签:

【中文标题】离开 Activity 时 Android 如何处理后台线程?【英文标题】:How does Android handle background threads when leaving an Activity? 【发布时间】:2010-10-10 00:22:25 【问题描述】:

我需要我的 android 应用在其活动被置于后台或被终止时将其状态保存到磁盘。有人建议我在调用 onPause() 时启动一个线程并在那里执行任何昂贵的 I/O 过程(请参阅Saving/loading document state quickly and robustly for image editor)。

操作系统会在什么情况下终止线程,这些情况发生的频率如何?

我认为这就像处理活动的方式一样,操作系统可以任意决定终止线程,但大多数情况下只会在资源极其有限时才这样做。不过,如果能找到一些具体的文档就好了。

通过使用一些测试代码,在 onPause() 中启动的后台线程将在我的设备上无限期地在后台运行(我尝试加载大量应用程序但无法将其杀死)。

对于我的特定应用程序,我正在编写一个位图编辑器,其中我使用命令模式和备忘录模式来允许撤消和重做编辑。我希望用户能够撤消/重做他们的编辑,甚至例如用户接到电话,活动在后台被终止。我能想到的最佳解决方案是使用后台线程在应用程序使用期间不断将我的命令和备忘录对象保存到磁盘,并在调用 onPause 时完成保存留在后台线程中的任何对象。在最坏的情况下,如果线程被杀死,我只会丢失一些编辑。

【问题讨论】:

【参考方案1】:

操作系统会在什么情况下终止线程,这些情况发生的频率如何?

操作系统不会杀死线程,除非它正在杀死进程——Android 不会对您自己创建的线程做任何事情。如果你是前台进程,你不会被杀死。 Android 在您失去前台后的几秒钟内(在onPause() 之后)杀死进程的几率微乎其微。可以在here 找到有关进程生命周期的文档——它有什么——。

【讨论】:

澄清一下,如果你在进程中创建了一个附加到某个activity的线程,那么这个线程会在activity被杀死的时候被杀死(即进程和线程是链接的)?跨度> @tiftuff:活动不是应用程序。活动是应用程序的一个组件。一个应用程序可能有许多组件,包括多个活动、服务等。当应用程序的最后一个组件被销毁时(例如,用户从一个小应用程序中的唯一活动按 BACK),该进程被指定用于回收或终止。不要泄漏线程。如果您启动线程,则必须安排它终止,因为 Android 不会为您终止它,除非终止进程,这可能在几周内不会发生。 谢谢。对于这个后台工作是否比线程更适合使用服务,您有什么建议吗? @memcom:线程和服务是正交的——选择一个并不能排除另一个的选择。就 UI 而言,服务会自动在后台运行。从线程的角度来看,它们在您的活动使用的同一主应用程序线程上运行。因此,服务所花费的时间会占用 UI,可能会导致应用程序无响应 (ANR) 异常。因此,我预计许多服务将使用AsyncTask 或它们自己的线程管理。换句话说,它不是“服务或线程”,而是“服务线程”。 @memcom:嗯,一如既往,这取决于。 :-) 如果任务是“一劳永逸”,在其构造函数中接收数据并且不再需要接触活动,从活动启动AsyncTask 应该没问题。但是,如果AsyncTask 需要使用活动,事情就会变得有点混乱,因为活动可能会消失(例如,方向改变)。有一些模式可以解决这个问题,但服务可能是您的另一种选择。最佳答案可能是IntentService,它有效地将ServiceAsyncTask 结合在一起,专为在后台线程上运行的命令而设计。【参考方案2】:

您的线程可能在活动被销毁后的任何时间被杀死,或者它可能永远不会被杀死。依赖这样的线程是一种非常糟糕的形式——你可能会得到一个半完成的操作,或者一个永远存在的线程。

如果您希望执行即使在没有前台活动时仍继续进行的后台操作,您几乎总是希望在服务中运行它。另一方面,该服务不太可能被杀死,但除非您使用“startForeground”,否则无法保证。这最终会向用户显示后台正在发生的事情的通知,但据我所知,这是运行保证不会被杀死的异步后台线程的唯一方法。

老实说,正确的答案是确保永远不会有任何需要很长时间才能保存的临时进程状态。如果您必须编写一个大文件来反映一些用户更改,请考虑维护一个“事务日志”,您可以使用它来创建可重新启动的保存操作。鉴于此,您可以安全地在服务中运行保存,并且知道即使它被杀死,它也会在资源可用时自动重新启动。

【讨论】:

线程将在大约 5 秒内终止,因为我等待确认我的数据已写入磁盘(价值约 2Mb)。我的自动保存加载器会考虑到线程可能在完成之前被杀死,用户可能会失去 30 秒的工作。您不认为为此启动服务有点重吗?我可以创建编辑操作的事务日志,但这些也需要在退出时保存到磁盘(我还能在哪里快速存储它?),用户可以进行大编辑然后被打断。 启动一项新服务绝对太重量级了。这是推荐的处理方式,例如,如果处理 Widget 通知需要超过一秒左右的时间。服务提供正是您正在寻找的东西 - 系统会意识到他们有充分的理由留下来,并会尽量避免先发制人地杀死他们,这样您就可以更有信心'将获得 5 秒的后台处理。听起来您已经为需要终止服务的罕见情况做好了准备,这就是您所需要的。【参考方案3】:

通常,将您的状态保存在 onPause 中是正确的做法,如果它很快的话。我认为进程被终止时没有明确记录,但是当您运行一些要求苛刻的应用程序(例如,在运行 Google 地球和浏览器之后)时,您有时会在 logcat 中看到它。

Android DevTools 中还有一个选项可以在您离开活动时自动销毁活动,尽管这可能不会扩展到流程。 (DevTools 在模拟器上,在一些 root 手机上)。

我认为您的方法听起来很合理 - 使用低优先级线程不断更新保存数据,并在 onPause 中为其赋予正常优先级,并在 onPause 中设置一个标志,告诉它在完成后终止。

显然,如果您在 onPause 之后立即进入 onResume(即当线程仍在忙于保存时),您需要确保不会遇到同步问题。

【讨论】:

以上是关于离开 Activity 时 Android 如何处理后台线程?的主要内容,如果未能解决你的问题,请参考以下文章

离开活动去另一个活动时,Xamarin Android Finalizer 没有被调用

Android中,activity生命周期是指啥?

Android中Activity源码中是如何对对话框Dialog进行处理的

Android中Activity运行时屏幕方向与显示方式详解

Android 全局弹窗(Dialog)快速实现

android对应生命周期中应该释放哪些资源