Android AsyncTask可长时间运行


90

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

理想情况下,应将AsyncTasks用于较短的操作(最多几秒钟)。如果需要使线程长时间运行,则强烈建议您使用java.util.concurrent pacakge提供的各种API,例如执行程序,ThreadPoolExecutor和FutureTask。

现在我的问题出现了:为什么?该doInBackground()函数运行在UI线程之外,因此如果在此处长时间运行该操作有何危害?


2
我在将应用程序(使用asyncTask)部署到实际设备时遇到的问题是,doInBackground如果不使用进度条,长时间运行的功能将冻结屏幕。
venkatKA,2012年

2
由于AsyncTask与启动它的Activity绑定在一起。因此,如果Activity被杀死,您的AsyncTask实例也可能被杀死。
IgorGanapolsky 2014年

如果我在服务中创建AsyncTask怎么办?那不能解决问题吗?
涡流

1
使用IntentService,完美的解决方案可在后台长时间运行。
Keyur Thumar'2

Answers:


120

这是一个非常好的问题,作为Android程序员,需要花费一些时间才能完全理解该问题。确实,AsyncTask有两个相关的主要问题:

  • 它们与活动生命周期联系不紧密
  • 它们非常容易造成内存泄漏。

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

AsyncTask和Activity生命周期

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

完成后,AsyncTask将不会更新新Activity的UI。实际上,它更新了不再显示的活动的前一个实例。这可能导致java.lang.IllegalArgumentException类型的异常:如果您使用例如findViewById在Activity中检索视图,则视图未附加到窗口管理器。

内存泄漏问题

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

但是,这意味着内部类将在其外部类实例(活动)上保留一个不可见的引用。

从长远来看,这会导致内存泄漏:如果AsyncTask持续很长时间,它将使活动保持“活动状态”,而Android希望摆脱它,因为它不再显示。该活动不能被垃圾收集,这是Android在设备上保留资源的主要机制。


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

我鼓励您下载RoboSpice Motivations应用程序,它确实对此进行了深入的解释,并提供了一些示例和演示有关进行某些后台操作的不同方式。


@Snicolas嗨。我有一个应用程序,它可以扫描NFC标签中的数据并将其发送到服务器。它在信号良好的区域工作正常,但在没有信号的地方,使Webcall保持运行的AsyncTask。例如,进度对话框运行几分钟,然后消失,屏幕变为黑色且无响应。我的AsyncTask是一个内部类。我正在编写处理程序以在X秒后取消任务。该应用似乎在扫描后数小时就将旧数据发送到服务器。难道是由于AsyncTask未完成,然后可能在几小时后完成?我将不胜感激。谢谢
turtleboy

跟踪以查看发生了什么。但是,是的,这很有可能!如果您很好地设计了异步任务,则可以适当地取消它,如果您不想迁移到RS或服务,那将是一个很好的起点……
Snicolas 2013年

@Snicolas感谢您的回复。我昨天在SO上发表了一篇文章,概述了我的问题,并显示了我编写的处理程序代码,试图在8秒后停止AsyncTask。如果有时间,您介意看看吗?从处理程序调用AsyncTask.cancel(true)是否可以正确取消任务?我知道我应该定期在doInBackgroud中检查iscancelled()的值,但是我认为这不适用于我的情况,因为我只是进行一行网络调用HttpPost而不是在UI上发布更新.AsyncTask是否有替代方案例如,是否可以从IntentService发出HttPost
turtleboy


您应该尝试一下RoboSpice(在github上)。;)
Snicolas 2013年

38

为什么呢?

因为AsyncTask默认情况下使用的不是您创建的线程池。切勿占用未创建的池中的资源,因为您不知道该池的要求是什么。如果该池的文档告诉您不这样做,则切勿占用未创建的池中的资源,如此处所示。

特别是从Android 3.2开始,AsyncTask默认情况下使用的线程池(对于android:targetSdkVersion设置为13或更高版本的应用程序)仅包含一个线程-如果无限期地占用该线程,则其他任何任务都不会运行。


感谢您的解释。.对于长时间运行的操作,我找不到真正错误的AsyncTasks(尽管我通常将这些委托给Services自己),但是您对绑定ThreadPool的争论(您确实可以这样做)无需对其大小做任何假设)。作为Anup关于使用服务的文章的补充:该服务本身也应该在后台线程上运行任务,而不是阻塞自己的主线程。选项可以是IntentService,或者对于更复杂的并发要求,可以应用自己的多线程策略。
晒太阳

即使我在服务中启动AsyncTask也一样吗?我的意思是,长期运行仍然会带来问题吗?
涡流

1
@eddy:是的,因为这不会改变线程池的性质。对于a Service,只需使用a Thread或a ThreadPoolExecutor
CommonsWare 2014年

谢谢@CommonsWare,这是最后一个问题,TimerTasks是否也具有AsyncTasks的相同缺点?还是完全不同的事情?
涡流2014年

1
@eddy:TimerTask来自标准Java,而不是Android。TimerTask已在很大程度上被弃用ScheduledExecutorService(尽管它的名字,它是标准Java的一部分)。两者都与Android无关,因此,如果您希望这些事情在后台运行,那么您仍然需要一项服务。而且,您确实应该考虑AlarmManager使用Android,因此您不需要仅在时钟上滴答作响的服务。
CommonsWare 2014年

4

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

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

因此,对于短期任务,请使用异步任务,因为它们会在您的产卵活动死亡后被操作系统杀死(通常不会在操作中死亡,但会完成其任务)。对于冗长而重复的任务,请改用服务。

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

AsyncTask超过几秒钟?

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


1

AsyncTask的问题在于,如果将其定义为活动的非静态内部类,则它将引用活动。在活动结束的情况下,异步任务的容器完成了,但AsyncTask的后台工作仍在继续,活动对象将不会被垃圾回收,因为存在对它的引用,这会导致内存泄漏。

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.