当离开一个Activity时,Android如何处理后台线程?

35

当Android应用程序的活动进入后台或被杀死时,我需要将其状态保存到磁盘。建议我在onPause()被调用时启动线程,并在其中执行任何昂贵的 I/O 操作(参见为图像编辑器快速且稳健地保存/加载文档状态)。

在哪些情况下,操作系统会杀死该线程,这些情况有多常见?

我认为它的处理方式类似于Activity,操作系统可以随意决定杀死线程,但通常只有在资源极度有限时才会这样做。不过最好能找到一些具体的文档资料。

通过测试代码进行实验,在onPause()中启动的后台线程将在我的设备上无限期地运行(我尝试了加载许多应用程序,但无法使其被杀死)。

对于我编写的具体应用程序,我正在编写一个位图编辑器,其中使用了命令模式和备忘录模式来允许撤销和重做操作。我希望用户能够在例如接电话并将活动置于后台时撤消/重做操作。我能想到的最好的解决方案是使用一个后台线程来不断将我的命令和备忘录对象保存到磁盘上,并在应用程序使用期间完成任何留存在后台线程中的对象的保存,如果onPause()被调用,则继续保存这些对象。在最坏的情况下,如果线程被杀死,我只会丢失一些编辑内容。

3个回答

21
操作系统只有在杀死进程时才会终止线程,除非你自己创建的线程。如果你是前台进程,不会被杀死。在你失去前台(经过`onPause()`)几秒钟后,Android杀死进程的概率微乎其微。有关进程生命周期的文档,请参见此处

2
只是为了澄清,如果您在附加到某些活动的进程中创建线程,则当活动被销毁时,该线程也将被终止(即进程和线程是相关联的)? - memcom
3
@tifftuff:一个Activity不是一个应用程序,而是应用程序的一个组件。一个应用程序可以有很多组件,包括多个活动、服务等。当应用程序的最后一个组件被销毁时(例如,在小应用程序的唯一活动中按下“返回”按钮),该进程将被标记为回收或终止。不要泄漏线程。如果你启动了线程,你必须安排它终止,因为Android不会为你终止它,除非通过终止进程,这可能需要几周时间。 - CommonsWare
谢谢。您是否有任何建议,关于在进行后台工作时是更适合使用服务还是线程? - memcom
3
线程和服务是相互独立的,选择其中一个并不会排除另一个的选择。服务自动在后台运行,就 UI 而言是如此。从线程的角度来看,它们在与活动相同的主应用程序线程上运行。因此,在服务中花费的时间将占用 UI,可能会导致“应用程序无响应”(ANR)异常。因此,我期望许多服务将使用 AsyncTask 或它们自己的线程管理机制。换句话说,这不是“服务或线程”,而是“服务 线程”。 - CommonsWare
谢谢。为了澄清,我的意思是,我是否应该真的使用 Service(它启动自己的 AsyncTask)来做我的后台工作,还是直接在 onPause() 函数中创建一个 AsyncTask 完成我需要完成的工作就可以了?我只是想知道是否有任何区别,因为实现一个 Service 会涉及到更多的工作。 - memcom
1
@memcom:嗯,像往常一样,这取决于情况。:-) 如果任务是“点火并忘记”,在其构造函数中接收数据,并且不再需要触摸活动,则从活动启动AsyncTask应该没问题。但是,如果AsyncTask需要使用活动,则会变得有些混乱,因为活动可能会消失(例如,方向更改)。有处理此问题的模式,但服务可能是您的另一个选择。最好的答案可能是IntentService,它有效地将ServiceAsyncTask结合起来,旨在在后台线程上运行命令。 - CommonsWare

4

在活动被销毁后,您的线程可能随时被终止,或者它可能永远不会被终止。依赖这样的线程是非常糟糕的做法 -- 您可能会得到一个未完成的操作,或者得到一个永远存在的线程。

如果您希望执行一个即使没有前台活动也可以继续进行的后台操作,则几乎总是要在Service中运行它。另一方面,服务不太可能被终止,但除非使用“startForeground”,否则不能保证。这将显示一个通知给用户,告诉他们有些东西在后台发生,但据我所知,这是运行异步后台线程的唯一不会被终止的保证方式。

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


线程将在大约5秒内终止,因为我等待确认我的数据已写入磁盘(大约2Mb)。我的自动保存程序将考虑到线程可能在完成之前被终止,用户可能会丢失30秒的工作。您不认为为此启动服务有点过重吗?我可以创建一个编辑操作的事务日志,但这些也需要在退出时保存到磁盘上(在哪里可以快速存储它?),而用户可能会进行大量编辑,然后被打断。 - memcom
1
启动一个新服务绝对不会太重。当处理小部件通知需要超过一秒钟左右时,这是推荐的做法。服务提供了确切你所需要的东西——系统将意识到它们有充分的理由保持运行,并尽量避免抢占式杀死它们,因此你可以更加自信地获得5秒的后台处理时间。听起来你已经为服务需要被杀死的罕见情况做好了准备,所以这就是你所需要的全部。 - beekeeper

1

通常情况下,如果在onPause中快速保存状态是正确的做法。我认为当进程被杀死时没有明确的文档记录,但有时你会在 logcat 中看到这个现象,特别是在运行一些要求高的应用程序后(比如 Google Earth 和浏览器)。

在 Android DevTools 中也有一个选项,可以在您导航离开活动时自动销毁它们,尽管这可能不适用于整个进程。 (DevTools 在模拟器和某些已 root 的手机上可用)。

我认为您的方法是合理的——使用低优先级线程不断更新保存的数据,并在 onPause 中将其设置为普通优先级,并设置一个标志,告诉它在完成后终止。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接