92

引用此处找到的 AsyncTask 的文档,它说:

AsyncTasks 最好用于短时间的操作(最多几秒钟)。如果您需要保持线程长时间运行,强烈建议您使用 java.util.concurrent 包提供的各种 API,例如Executor、ThreadPoolExecutor 和 FutureTask。

现在我的问题出现了:为什么?该doInBackground()函数在 UI 线程之外运行,那么在这里长时间运行操作有什么危害?

4

4 回答 4

121

这是一个非常好的问题,作为一个 Android 程序员需要时间来完全理解这个问题。事实上 AsyncTask 有两个相关的主要问题:

  • 它们与活动生命周期的联系很差
  • 它们很容易造成内存泄漏。

RoboSpice Motivations 应用程序(可在 Google Play 上获得)中,我们详细回答了这个问题。它将深入了解 AsyncTasks、Loaders、它们的特性和缺点,并向您介绍网络请求的替代解决方案:RoboSpice。网络请求是 Android 中的常见需求,本质上是长时间运行的操作。这是该应用程序的摘录:

AsyncTask 和 Activity 生命周期

AsyncTasks 不遵循 Activity 实例的生命周期。如果您在 Activity 中启动 AsyncTask 并旋转设备,则 Activity 将被销毁并创建一个新实例。但是 AsyncTask 不会死。它将继续存在,直到完成。

当它完成时,AsyncTask 不会更新新 Activity 的 UI。实际上,它会更新不再显示的活动的前一个实例。这可能会导致 java.lang.IllegalArgumentException: View not attach to window manager 类型的异常,例如,如果您使用 findViewById 来检索 Activity 中的视图。

内存泄漏问题

将 AsyncTasks 创建为活动的内部类非常方便。由于 AsyncTask 需要在任务完成或正在进行时操作 Activity 的视图,因此使用 Activity 的内部类似乎很方便:内部类可以直接访问外部类的任何字段。

然而,这意味着内部类将在其外部类实例上持有一个不可见的引用:Activity。

从长远来看,这会产生内存泄漏:如果 AsyncTask 持续很长时间,它会保持活动“活动”,而 Android 想要摆脱它,因为它不再显示。Activity 不能被垃圾收集,这是 Android 在设备上保留资源的中心机制。


将 AsyncTasks 用于长时间运行的操作确实是一个非常非常糟糕的主意。不过,它们适用于短暂的生命周期,例如在 1 或 2 秒后更新视图。

我鼓励您下载RoboSpice Motivations 应用程序,它确实深入地解释了这一点,并提供了执行某些后台操作的不同方法的示例和演示。

于 2012-10-26T06:45:55.653 回答
38

为什么 ?

因为AsyncTask,默认情况下,使用您没有创建的线程池。永远不要占用不是您创建的池中的资源,因为您不知道该池的要求是什么。如果该池的文档告诉您不要占用您未创建的池中的资源,则永远不要占用该池中的资源,就像这里的情况一样。

特别是,从 Android 3.2 开始,AsyncTask默认使用的线程池(对于android:targetSdkVersion设置为 13 或更高版本的应用程序)只有一个线程——如果你无限期地占用这个线程,你的其他任务都不会运行。

于 2012-10-09T11:00:15.473 回答
4

Aysnc 任务是专门的线程,仍然可以与您的应用程序 GUI 一起使用,但同时保持 UI 线程的资源繁重任务。因此,当更新列表、更改视图等需要您执行一些获取操作或更新操作时,您应该使用异步任务,以便您可以将这些操作保持在 UI 线程之外,但请注意这些操作仍以某种方式连接到 UI .

对于不需要更新 UI 的长时间运行的任务,您可以改用服务,因为即使没有 UI,它们也可以生存。

因此,对于短任务,请使用异步任务,因为它们可能会在您的生成活动终止后被操作系统杀死(通常不会在操作过程中终止,但会完成其任务)。对于冗长且重复的任务,请改用服务。

有关更多信息,请参阅线程:

AsyncTask 超过几秒钟?

即使活动已销毁,AsyncTask 也不会停止

于 2012-10-09T10:11:44.937 回答
1

AsyncTask 的问题在于,如果它被定义为 Activity 的非静态内部类,它就会有对 Activity 的引用。在异步任务容器中的activity完成,但AsyncTask中的后台工作仍在继续的情况下,activity对象不会被垃圾回收,因为它有一个引用,这会导致内存泄漏。

解决此问题的解决方案是将异步任务定义为活动的静态内部类并使用对上下文的弱引用。

但是,将它用于简单快速的后台任务仍然是一个好主意。要使用干净的代码开发应用程序,最好使用RxJava运行复杂的后台任务并使用结果更新 UI。

于 2018-03-28T07:46:20.327 回答