我对android中这种简单的常见情况有疑问。
我们有一个主要活动,我们调用AsyncTask以及mainactivity的引用,以便AsyncTask可以更新MainActivity上的视图。
我会将事件分解为步骤
上述解决方案是按照“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会发生什么?
谢谢,我为问题的长度道歉。
答案 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
/ onDestroy
,onSave/RestoreInstanceState
)对所需行为进行编码。
关于原始AsyncTask
,我不能100%确定会发生哪些选项:
AsyncTask
,因为唯一持有对它的引用的对象(原始Activity
)被销毁AsyncTask
对象的引用,阻止其垃圾回收,有效地让AsyncTask
在后台完成无论哪种方式,最好手动中止/暂停和重启/恢复AsyncTask
(或将其移交给新Activity
),或使用Service
代替
答案 2 :(得分:1)
这如何解决这种情况?
没有。
当垃圾收集器确定指示对象是弱可达时,WeakReference
的指示对象设置为null
。当活动暂停时不会发生这种情况,并且当活动被销毁并且框架丢弃对它的所有引用时,不一定会立即发生。如果GC尚未运行,AsyncTask
完全有可能完成WeakReference
仍然包含对死活动的引用。
不仅如此,但这种方法无法阻止AsyncTask
无用地消耗CPU。
更好的方法是让Activity
在适当的拆解生命周期方法中保持对AsyncTask
和cancel(...)
的强引用。 AsyncTask
应监控isCancelled()
,如果不再需要,则停止工作。
如果您希望AsyncTask
在配置更改后生存(但不其他形式的活动销毁),您可以将其托管在保留的片段中。