60

我有一个关于 android 中这种简单频繁发生的情况的问题。

我们有一个 main 活动,我们调用一个 AsyncTask 以及 mainactivity 的引用,以便 AsyncTask 可以更新 MainActivity 上的视图。

我会将事件分解为步骤

  • MainActivity 创建一个 AyncTask,将其引用传递给它。
  • AysncTask ,启动它的工作,例如下载十个文件
  • 用户更改了设备的方向。这会导致 AsyncTask 中出现孤立指针
  • 当 AsyncTask 完成并尝试访问活动以更新状态时,它会因为空指针而崩溃。

上述解决方案是按照“Pro Android 4”一书的建议在 AsyncTask 中保留 WeakReference

WeakReference<Activity> weakActivity;

in method onPostExecute

Activity activity = weakActivity.get();
if (activity != null) {
   // do your stuff with activity here
}

这如何解决这种情况?

我的问题是,如果我的 asynctask 正在下载十个文件,并且在完成 5 后重新启动活动(由于方向更改),那么我的 FileDownloadingTask 是否会再次被调用?

最初调用的先前 AsyncTask 会发生什么?

谢谢,我为这个问题的长度道歉。

4

3 回答 3

36

这如何解决这种情况?

WeakReference允许进行垃圾收集Activity,因此您没有内存泄漏。

空引用意味着AsyncTask 不能盲目地尝试更新不再附加的用户界面,这会引发异常(例如未附加到窗口管理器的视图)。当然,您必须检查 null 以避免 NPE。

如果我的 asynctask 正在下载十个文件,并且在完成 5 后重新启动活动(由于方向更改),那么我的 FileDownloadingTask 是否会再次被调用?

取决于您的实现,但可能是的 - 如果您不故意做一些事情来使重复下载变得不必要,例如在某处缓存结果。

AsyncTask最初调用的前一个会发生什么?

在早期版本的 Android 中,它会运行到完成,下载所有文件只是为了丢弃它们(或者可能缓存它们,具体取决于您的实现)。

在较新的 Android 中,我怀疑AsyncTask' 与启动它们的设备一起被杀死Activity,但我怀疑的基础只是RoboSpice的内存泄漏演示(见下文)实际上并没有在我的 JellyBean 设备上泄漏。

如果我可以提供一些建议:AsyncTask不适合执行可能长时间运行的任务,例如网络。

IntentService如果您可以接受单个工作线程,则这是一种更好(并且仍然相对简单)的方法。如果您想控制线程池,请使用(本地)Service- 并注意不要在主线程上工作!

如果您正在寻找一种在后台可靠地执行网络的方法, RoboSpice似乎不错(免责声明:我没有尝试过;我不附属)。Play 商店中有一个RoboSpice Motivations 演示应用程序,它通过演示所有可能出错的东西来解释为什么AsyncTask你应该使用它- 包括 WeakReference 解决方法。

另请参阅此线程:AsyncTask 真的在概念上存在缺陷还是我只是遗漏了什么?

更新:

我创建了一个github 项目IntentService,其中包含另一个 SO 问题(如何修复 android.os.NetworkOnMainThreadException? )的下载示例,但我认为它在这里也很重要。它的另一个优点是,通过返回结果onActivityResult,当您旋转设备时正在运行的下载将传递给重新启动的Activity.

于 2013-08-19T08:26:02.587 回答
7

该类WeakReference基本上只是阻止 JRE 增加给定实例的引用计数器。

我不会进入 Java 的内存管理并直接回答您的问题:通过提供一种了解其父活动是否仍然有效的方法来WeakReference解决这种情况。AsyncTask

方向更改本身不会自动重新启动AsyncTask. onCreate您必须使用已知机制 ( / onDestroy, )对所需行为进行编码onSave/RestoreInstanceState

关于原来的AsyncTask,我不能 100% 确定这些选项中的哪一个会发生:

  • Java 会停止线程并处理AsyncTask,因为唯一持有对它的引用的对象(原始Activity)被销毁
  • 或者一些内部 Java 对象维护对该AsyncTask对象的引用,阻止其垃圾收集,有效地将AsyncTask在后台完成

无论哪种方式,最好AsyncTask手动中止/暂停并重新启动/恢复(或将其交给新的Activity),或者使用 aService代替。

于 2013-08-18T20:50:45.423 回答
2

这如何解决这种情况?

它没有。

当垃圾收集器确定所指对象是弱可达时,a 的引用对象WeakReference被设置为。null这不会在活动暂停时发生,也不一定在活动被销毁并且框架丢弃对它的所有引用时立即发生。如果 GC 没有运行,则完全有可能AsyncTask在其WeakReference仍然包含对已死活动的引用时完成。

不仅如此,这种方法并不能防止无AsyncTask用地消耗 CPU。

更好的方法是在适当的拆卸生命周期方法中保持对和itActivity的强引用。如果不再需要,应该监视并停止工作。AsyncTaskcancel(...)AsyncTaskisCancelled()

如果您希望AsyncTask在配置更改(但不是其他形式的活动破坏)中存活下来,您可以将其托管在保留的片段中。

于 2017-04-24T06:09:04.967 回答