2013-08-18 38 views
44

我有一个关于android中这种简单的频繁发生的情况的问题。Android中的WeakReference/AsyncTask模式

我们有一个主要活动,我们调用的AsyncTask非常久远在MainActivity的参考,以便该的AsyncTask可以更新在MainActivity的意见。

我会分解事件转换为步骤

  • MainActivity创建AyncTask,通过其引用。
  • AysncTask,启动它的工作,下载十个文件,例如
  • 用户更改设备的方向。这导致AsyncTask中的孤儿指针当AsyncTask完成并尝试访问活动以更新状态时,由于空指针而崩溃。

以上部分的解决办法是保持在一个的AsyncTask的WeakReference所推荐的书“临的Android 4.0”

WeakReference<Activity> weakActivity; 

in method onPostExecute 

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

这是如何解决的情况呢?我的问题是,如果我的asynctask正在下载10个文件,并且在5完成后活动重新启动(因为方向改变),那么我的FileDownloadingTask会再次被调用吗?

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

谢谢,我对问题的长度表示歉意。

+5

感谢您发布这样一个结构合理,措辞严谨的问题。 – Travis

回答

32

这是怎么解决的?

WeakReference允许Activity被垃圾收集,所以你没有内存泄漏。

空引用意味着该AsyncTask不能盲目地尝试更新一个用户接口,不再连接,这将抛出异常(例如图中未附接至窗口管理器)。当然,你必须检查空值以避免NPE。

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

取决于具体的实现,但或许是肯定的 - 如果你不刻意做一件让重复下载不必要的,如某处缓存结果。

以前调用的AsyncTask会发生什么?

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

在较新的Android的我很怀疑AsyncTask的正在与开始他们Activity一起遇难的,但我的怀疑依据仅仅是内存泄漏演示的为RoboSpice(见下文)不上实际泄露我的软糖装置。

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

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

RoboSpice似乎不错,如果你正在寻找一种方式来可靠地执行在后台联网(声明:我还没有尝试过,我不隶属于)。在Play商店中有一个RoboSpice Motivations demo app,它解释了为什么您应该通过演示所有可能出现的错误信息AsyncTask来使用它 - 包括WeakReference解决方法。

也看到这个线程:Is AsyncTask really conceptually flawed or am I just missing something?

更新:

我创建了一个github project使用IntentService另一个SO问题(How to fix android.os.NetworkOnMainThreadException?)下载的例子,但它也与此有关,我想。它具有的附加优点是,通过onActivityResult返回结果,当您旋转设备时正在进行的下载将传送到重新启动的Activity

+2

Little update:现在RxJava是在后台运行任务的好方法,关闭主线程并在主线程上传递结果(因此您可以更新UI等)。另一种选择是使用EventBus, – AgentKnopf

6

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

我不会去到Java的内存管理和直接回答你的问题:WeakReference通过提供AsyncTask的方式来学习,如果它的父活动仍然有效解决的情况。

方向更改本身不会自动重新启动AsyncTask。您必须使用已知机制编码期望的行为(onCreate/onDestroy,onSave/RestoreInstanceState)。

关于原AsyncTask,我不是100%肯定它的这些选项会发生:

  • 无论是Java的停止线并配置AsyncTask,因为只有物体保持对它的引用(原来Activity)被破坏
  • 或者一些内部Java对象保持到AsyncTask对象的引用,阻断其垃圾回收,有效地离开AsyncTask在后台完成

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

+0

当您使用AsyncTask Activity的内部类时,WeakReference没有用处 – Qamar

1

这是如何解决这种情况的?

它没有。

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

不仅如此,而且这种方法不会阻止AsyncTask无用地占用CPU。

更好的方法是让Activity在合适的拆卸生命周期方法中保持对AsyncTaskcancel(...)的强烈参考。 AsyncTask应该监视isCancelled(),如果不再需要,则停止工作。

如果你想要一个AsyncTask跨越配置变化(但不其他活动形式破坏的)生存,你可以在保留片段承载它。