为什么AsyncTask不适用于长时间运行的操作

3

我已经阅读了以下链接中提到的两个原因/问题:
(请在下面链接中阅读两个原因)
Android AsyncTask for Long Running Operations
1. “如果您在 Activity 中启动 AsyncTask 并旋转设备,则 Activity 将被销毁并创建一个新实例。但是,AsyncTask 将不会终止”:
假设我将我的活动方向设置为纵向。这个问题仍然存在吗?

2. 内存泄漏问题:
内部类将保留对其外部类实例(即 Activity)的不可见引用。
如果我没有使用内部 AsyncTask 而是创建了单独的类,另外如果我使用弱引用。


如果您将活动的方向设置为纵向,则在旋转设备时,该活动不会被销毁,因此不会创建新实例。 - Akshay Bhat 'AB'
2
可能是Android AsyncTask for Long Running Operations的重复问题。 - mjp66
@rathee ashish,我建议在Service中使用AsyncTask - Vladimir Markeev
@AkshayBhat 我也想知道为什么不应该使用asynctask来处理长时间运行的进程的原因。 - rathee ashish
@AkshayBhat,这并不能真正帮助你,因为通常服务与启动它的活动共享事件线程(除非服务在另一个进程中)。因此,该服务实际上是同步的。这也是为什么例如IntentService会生成一个工作线程来处理请求的原因。 - dhke
显示剩余6条评论
5个回答

4
你提到的问题只会在AsyncTask的生命周期没有得到正确处理时出现,这主要是由于缺乏对其工作原理的理解。
AsyncTask是一个在单独线程上运行代码的包装器。它类似于纯Java中提交给ExecutorService的Runnable,具有额外的“前”和“后”钩子功能,可以在主线程上运行。因此,它基本上是Thread、Runnable和Handler设置的增强版本。
默认情况下,AsycTask共享一个线程,因此不建议用于长时间运行的任务。因为当多个任务共享单个后台线程时,长时间运行的任务可能会阻塞其他任务。但是,AsyncTask也可以在自定义Executor上运行,从而消除了共享工作线程的限制。
所有这些意味着AsyncTask的设计并不限制其用于长时间运行的任务。
您可以使用后台服务在单独的ThreadPoolExecutor上运行一些持续处理的任务。
您可以使用AsyncTask在Fragment中加载最新的新闻,并在Fragment的onDestroy()被调用时取消任务,因为它已经不再有意义。
因此,“AsyncTask应该运行多长时间”的答案完全取决于使用上下文。

Android文档中提到:“syncTasks理想情况下应该用于短时间操作(最多几秒钟)。如果您需要长时间运行线程,强烈建议使用java.util.concurrent包提供的各种API,例如Executor、ThreadPoolExecutor和FutureTask。” 但是为什么呢? - rathee ashish
@ratheeashish 这是因为“默认情况下”所有任务都在单个线程上调度,而长时间的任务可能会剥夺其他任务运行的机会。但是AsyncTask还具有在您的“自定义”执行器上运行的功能,通过它您可以独立地运行它,而不影响其他任务。 - S.D.

1
  1. 设置方向将起作用,因为锁定为竖屏意味着没有方向更改,也就是说没有因此而导致的生命周期重新创建。但是,如果活动暂停了很长时间,它仍然可能被销毁,因此这不是确保100%工作的好方法。相反,您可以尝试使用服务或无界面片段

  2. 根据帖子,具有弱引用将解决内存问题


  1. 内部类将持有其外部类实例的不可见引用:但如果我为异步任务使用单独的类,内存泄漏问题仍然存在吗?
- rathee ashish
请查看此帖子了解弱引用和内存泄漏:https://dev59.com/DmMl5IYBdhLWcg3wkXtx?rq=1 - Lars Celie
如果您暂停应用程序或堆叠某些视图,则底层活动可能会被系统销毁(例如,如果需要一些内存)。因此,如果重新打开视图,则将重新创建您的视图。如果我们的AsyncTask设计不良,则会出现泄漏问题。 - Christopher
@Christopher 谢谢,我会把它添加到帖子中。 - Lars Celie

1
< p > AsyncTask 的另一个问题: 结果丢失

是的,你说过:

假设我将我的活动方向设置为纵向。 这个问题还会存在吗?

但是,Activity 不仅可以因旋转而重新创建。例如,如果系统中没有足够的资源,操作系统可能会销毁您的 Activity。

因此,对于长时间运行的操作,AsyncTask 在 Activity 重新创建后,在 onPostExecute() 中可能会有无效的 Activity 引用,存在很高的风险。

另一个问题: 并发性

new AsyncTask1().execute();
new AsyncTask2().execute();

这两个任务会同时运行吗,还是AsyncTask2会在AsyncTask1完成后启动?嗯...这取决于API级别。
关于API级别...
在API 1.6(甜甜圈)之前:任务是按顺序执行的。这意味着一个任务不会在前一个任务完成之前启动。
API 1.6到API 2.3(姜饼)之间:Android开发团队更改了AsyncTasks,使它们可以在单独的工作线程上并行启动。
API 3.0(蜂巢):AsyncTasks再次按顺序执行。当然,Android团队提供了让它们并行运行的可能性。这是通过方法executeOnExecutor(Executor)完成的。查看API文档以获取更多信息。

如果Activity在前台,则Activity不会被销毁,因为它的线程优先级是THREAD_PRIORITY_FOREGROUND。而异步任务使用的是THREAD_PRIORITY_BACKGROUND的工作线程。在这种情况下,不需要担心,异步任务也应该自行终止。如果有错误,请纠正我。 - rathee ashish
@ratheeashish 但无论如何,即使您的任务被仔细杀死,您的任务也将停止。如果您使用服务,即使活动被销毁,您的任务也将继续工作。 - Suvitruf - Andrei Apanasik

0

AsyncTask有以下缺点。

1. 内存泄漏:在内部类和单独的类中,您都会向AsyncTask提供对活动的引用以进行回调,在这两种情况下,AsyncTask都不会释放活动的引用以进行GC,从而导致内存泄漏。

2. GC:如果AsyncTask正在运行,尽管调用活动已被销毁,它将限制GC不运行,直到它完成其进程。

3. 在方向更改时,活动重新创建,因为Asynchtask将在后台运行,当它完成操作时,它将尝试更新UI,这会导致IllegalStateException,因为活动未附加到窗口。

因此,最好使用服务来进行长时间运行的后台处理,而不是使用AsyncTask。


对于内存泄漏问题:如果我使用弱引用,引用问题仍然存在吗? - rathee ashish
  1. 关于方向:我设置了固定方向。"IllegalStateException" 不会出现吗?
- rathee ashish
假设您正在异步任务中上传图像...即使活动被销毁,它仍然运行是一件好事吗? - rathee ashish
如果您可以使用弱引用,那是一个好的实践,但是现在您需要小心了。在传递结果之前,您需要检查活动引用是否为空,否则会出现NullPointerException。 - VikasGoyal
如果您仍在检查引用是否为空,则表示您没有在活动上收到回调。 - VikasGoyal
显示剩余3条评论

0

这个主题周围有很多迷信,很难知道从哪里开始。 AsyncTask 只是标准任务队列中的一个小糖果,使用它或不使用它与其他事情相比并没有太大区别。

例如,方向更改的问题并不存在。您可以在活动首次启动时启动 AsyncTask,然后下一次不启动它。(并记住,配置中的其他更改也可能重新启动您的活动)。

弱引用是完全过度的,可能对您毫无帮助。您需要对当前活动有一个引用(那么弱引用将无法工作),或者不需要(那么只需不保留任何引用即可)。

最重要的是,你的问题中缺少了你实际想要实现什么。

想象一下当你的应用程序正在运行时,有人接听电话,并在一段时间后回到它。然后尝试回答以下问题: - 15分钟前的结果是否相关?还是任务将被重新启动? - 六小时前呢? - 如果后台任务被中断,会发生什么不好的事情吗? - 用户是否期望任务完成?(他按下“确定”并等待确认出现吗?)。

然后您可以提出更精确的问题。 AsyncTask 可以在任何情况下使用,但通常不使用它比正确使用它更简单。

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