android中的WeakReference / AsyncTask模式

时间:2013-08-18 08:50:13

标签: java android android-asynctask weak-references

我对android中这种简单的常见情况有疑问。

我们有一个主要活动,我们调用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正在下载10个文件,并且在完成5后重新启动活动(因为方向改变),那么我的FileDownloadingTask会再次被调用吗?

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

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

3 个答案:

答案 0 :(得分:33)

  

这如何解决这种情况?

WeakReference允许Activity被垃圾回收,因此您不会有内存泄漏。

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

  

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

取决于您的实施,但可能是 - 如果您没有刻意做一些事情来重复下载,例如在某处缓存结果。

  

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

在Android的早期版本中,它将运行完成,只下载所有文件以将其丢弃(或者根据您的实现缓存它们。)

在较新的Android版本中,我怀疑AsyncTask是否与Activity一起被杀死,但我怀疑的基础只是记忆 - RoboSpice的泄漏演示(见下文)实际上并没有在我的JellyBean设备上泄漏。

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

如果您可以接受单个工作线程,那么

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

如果你正在寻找一种在后台可靠地执行网络的方法,那么

RoboSpice似乎很好(免责声明:我没有尝试过;我没有附属)。游戏商店中有RoboSpice Motivations demo app解释为什么你应该通过演示AsyncTask可能出错的所有内容来使用它 - 包括WeakReference解决方法。< / p>

另见此主题:Is AsyncTask really conceptually flawed or am I just missing something?

更新:

我创建了一个github project,其中包含使用IntentService下载另一个SO问题(How to fix android.os.NetworkOnMainThreadException?)的示例,但我认为这也与此相关。它还有一个额外的好处,即通过onActivityResult返回结果,当您旋转设备时正在进行的下载将传递给重新启动的Activity

答案 1 :(得分:7)

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

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

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

关于原始AsyncTask,我不能100%确定会发生哪些选项:

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

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

答案 2 :(得分:1)

  

这如何解决这种情况?

没有。

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

不仅如此,但这种方法无法阻止AsyncTask无用地消耗CPU。

更好的方法是让Activity在适当的拆解生命周期方法中保持对AsyncTaskcancel(...)的强引用。 AsyncTask应监控isCancelled(),如果不再需要,则停止工作。

如果您希望AsyncTask在配置更改后生存(但其他形式的活动销毁),您可以将其托管在保留的片段中。