为啥 AsyncTask 不适合长时间运行的操作

Posted

技术标签:

【中文标题】为啥 AsyncTask 不适合长时间运行的操作【英文标题】:Why AsyncTask are not prefered for Long Running Operations为什么 AsyncTask 不适合长时间运行的操作 【发布时间】:2016-02-22 22:31:07 【问题描述】:

我已阅读以下两个原因/问题:(请阅读以下链接中的两个原因)android AsyncTask for Long Running Operations 1.“如果您在 Activity 中启动 AsyncTask 并旋转设备,则 Activity 将被销毁并创建一个新实例。但 AsyncTask 不会死”:假设我已设置我的活动方向是纵向的。这个问题还会存在吗? 2. 内存泄漏问题: 内部类将在其外部类实例上保存一个不可见的引用:Activity。如果我不使用 Inner AsyncTask 而是创建单独的类怎么办。另外,如果我使用弱引用。

【问题讨论】:

如果你将活动的方向设置为纵向,如果你旋转设备,活动不会被破坏,所以没有新的实例。 @AkshayBhat 你能告诉我“为什么 AsyncTask 不适合长时间运行的操作” @rathee ashish 不是已经在您的私人链接中向您解释了吗? Android AsyncTask for Long Running Operations的可能重复 据我所知,AsyncTasks 是为网络操作而设计的,Android 开发人员提到不要将它用于长操作的异步任务。当您想要进行长时间运行的操作时,服务会派上用场。 【参考方案1】:

您提到的问题仅在 AsyncTask 的生命周期处理不当时才会出现,主要是由于对它们的工作原理缺乏了解。

AsyncTask 是用于在单独线程上运行代码的包装器。它类似于普通Java 的Runnable 提交给ExecutorService,附加了“pre”和“post”钩子在主线程上运行的特性。所以,它基本上是ThreadRunnableHandler 设置的增强版。

默认情况下,AsycTask 共享一个线程,因此不建议用于长时间运行的任务。因为当多个任务共享单个后台线程时,长时间运行的任务可能会阻塞其他任务。但是,AsycTask 也可以在自定义的Executor 上运行,消除了共享工作线程的这种限制。

这意味着 AsyncTask 自己的设计不会限制其用于长时间运行的任务。

您可以让后台 Service 在单独的 ThreadPoolExecutor 上使用 AsyncTasks 运行一些连续处理。

您可以使用 AsyncTask 让 Fragment 加载最新消息,当调用 Fragment 的 onDestroy() 时,您取消任务,因为它不再有意义。

因此,“AsyncTask 应该运行多长时间”的答案完全取决于使用上下文。

【讨论】:

Android 文档说:“syncTasks 最好用于短时间的操作(最多几秒钟)。如果您需要保持线程长时间运行,强烈建议您使用java.util.concurrent 包提供的各种 API,例如 Executor、ThreadPoolExecutor 和 FutureTask。”但为什么呢? @ratheeashish 这是因为“默认情况下”所有任务都安排在单线程上,而长任务可能会剥夺其他人运行的机会。但是 AsyncTask 也有一个功能可以在你的“自定义”执行器上运行,你可以使用它独立于其他任务运行它。【参考方案2】:

AsyncTask 的其他问题:丢失结果

是的,你说:

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

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

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

另一个问题:并行性

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

这两个任务会同时运行还是会在 AsyncTask1 完成时启动 AsyncTask2? 嗯...这取决于 API 级别

关于 API 级别...

在 API 1.6 (Donut) 之前:任务是串行执行的。这意味着在前一个任务完成之前不会开始任务。

API 1.6 到 API 2.3(Gingerbread):Android 开发团队更改了 AsyncTasks 以使它们可以在单独的工作线程上并行启动。

API 3.0 (Honeycomb):再次串行执行的 AsyncTasks。当然,Android 团队提供了让它们并行运行的可能性。这是通过executeOnExecutor(Executor) 方法完成的。查看 API 文档了解更多信息。

【讨论】:

如果 Activity 在前台,那么还有更多活动将是最后一个被销毁的(THREAD_PRIORITY_FOREGROUND)......其中异步任务使用带有(THREAD_PRIORITY_BACKGROUND)的工作线程.....在这种情况下没有需要担心......异步任务也应该被杀死......不确定......如果有错误请更正 @ratheeashish 但无论如何。即使您的任务会被小心地杀死,您的任务也会被停止。如果您将使用 Service,即使 Activity 将被销毁,您的 Task 也将继续工作。【参考方案3】:

    设置方向将起作用,因为锁定为纵向意味着没有方向更改,这意味着没有生命周期重新创建。但是,如果一个活动被暂停很长时间,它仍然可以被破坏,所以这不是确保它 100% 工作的好方法。你可以试试a service or a headless fragment。

    根据this 的帖子,弱引用将解决内存问题

【讨论】:

2.内部类将在其外部类实例上保留一个不可见的引用:但是如果我对异步任务使用单独的类,内存泄漏问题仍然存在吗? 查看此帖子以了解弱引用和内存泄漏:***.com/questions/18297378/… 至第 1 点:如果您暂停应用程序或堆叠一些视图,系统可能会破坏底层活动(例如,如果需要一些内存)。因此,如果您重新打开视图,您的视图将被重新创建。如果我们的 AsyncTask 设计不合理,就会出现泄漏。 @Christopher 谢谢,我会把它添加到帖子中【参考方案4】:

AsyncTask 有以下缺点。

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

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

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

因此,如果您将 Service 用于长时间运行的后台进程而不是 AsyncTask,效果会更好。

【讨论】:

1.对于内存泄漏:如果我使用弱引用,引用问题仍然存在吗? 3.关于方向:我设置了固定方向。 “IllegalStateException”不会出现? 2.假设您正在异步任务中上传图像....即使在活动被破坏后它仍在运行,这是一件好事? 1.如果您可以使用周参考,这是一个很好的做法,但您现在必须小心,现在您需要在传递结果之前检查活动参考是否为空,否则您会得到 NullPointerException。 3.如果您修复了活动的方向,那么如果您的情况允许,那么您限制了功能,那么没有问题。【参考方案5】:

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

例如,方向改变的问题是不真实的。您可以在活动第一次启动时启动AsyncTask,而下次不再启动它。 (请记住,配置中的其他更改也可以重新启动您的活动)。

弱引用完全是矫枉过正,可能会让你一事无成。您需要对当前活动的引用(然后弱引用将不起作用)或不需要(然后,根本不持有任何引用)。

您的问题中最重要但最缺失的是您真正想要完成的任务是什么?

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

然后你可以问一个更精确的问题。 AsyncTask可以在任何场景中使用,但通常不使用它比正确使用它更简单。

【讨论】:

以上是关于为啥 AsyncTask 不适合长时间运行的操作的主要内容,如果未能解决你的问题,请参考以下文章

Android AsyncTask 用于长时间运行的操作

为啥Android的AsyncTask不适合执行长时间操作的任务

执行后台任务——AsyncTask 的替代方案?

与 Java 和 Python 相比,为啥每次使用 Cmake 运行 C++ 程序都需要这么长时间?

为啥网站后台登录总是过不长时间就自动退出重新登录

为啥我的 SELECT 查询不需要我的 UPDATE 查询需要这么长时间?