Android中的WeakReference / AsyncTask模式
我有一个关于这个简单的经常发生的情况在Android的问题。
我们有一个主要的活动,我们调用一个AsyncTask以及mainactivity的引用,以便AsyncTask可以更新MainActivity上的视图。
我将把事件分解成几个步骤
- MainActivity创build一个AyncTask,将其引用传递给它。
- AysncTask,开始工作,例如下载十个文件
- 用户改变了设备的方向。 这会导致AsyncTask中的孤立指针
- 当AsyncTask完成并尝试访问活动来更新状态时,由于空指针,它崩溃。
上面的解决scheme是在“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会发生什么?
谢谢,我对这个问题的长度表示抱歉。
这是怎么解决的?
WeakReference
允许Activity
被垃圾收集,所以你没有内存泄漏。
空引用意味着AsyncTask
不能盲目地尝试更新不再附加的用户界面,这将引发exception(例如,视图不附加到窗口pipe理器)。 当然,你必须检查空,以避免NPE。
如果我的asynctask正在下载十个文件,并且在5完成后重新启动活动(因为方向改变),那么我的FileDownloadingTask会再次被调用吗?
取决于你的实现,但可能是的 – 如果你不刻意做一些重复下载不必要的,如caching结果的地方。
之前调用的
AsyncTask
会发生什么?
在早期版本的Android中,它会运行完成,下载所有文件只会将其扔掉(或者可能caching它们,具体取决于您的实现)。
在较新的Android中,我怀疑AsyncTask
是否与启动它们的Activity
一起被杀死,但是我怀疑的原因只是RoboSpice的内存泄露演示(见下文)实际上并没有泄漏到我的JellyBean设备上。
如果我可以提供一些build议: AsyncTask
不适合执行可能长时间运行的任务,如联网。
IntentService
是一个更好的(也是相对简单的)方法,如果一个工作者线程是可以接受的。 如果你想控制线程池,使用(本地) Service
– 注意不要在主线程上工作!
RoboSpice似乎很好,如果你正在寻找一种可靠地在后台执行联网的方式(免责声明:我还没有尝试过,我不隶属于)。 在Play商店中有一个RoboSpice Motivations演示应用程序 ,它解释了为什么您应该通过演示AsyncTask
可能出错的所有东西(包括WeakReference解决方法)来使用它。
另请参阅此线程: 是AsyncTask真的在概念上有缺陷,或者我只是失去了一些东西?
更新:
我创build了一个github项目 ,下载使用IntentService
为另一个SO问题( 如何解决android.os.NetworkOnMainThreadException? ),但它也是相关的,在这里,我认为。 它还有另外一个好处,就是通过onActivityResult
返回结果,当你旋转设备的时候,一个正在下载的下载文件将会传递给重新开始的Activity
。
WeakReference
类基本上阻止JRE增加给定实例的引用计数器。
我不会进入Java的内存pipe理并直接回答你的问题: WeakReference
通过提供AsyncTask
来了解其父活动是否仍然有效,从而解决了这种情况。
方向更改本身不会自动重新启动AsyncTask
。 您必须使用已知的机制( onCreate
/ onDestroy
, onSave/RestoreInstanceState
)编写所需的行为。
关于原始的AsyncTask
,我不是100%确定哪个选项会发生:
- Java停止线程并处置
AsyncTask
,因为唯一持有引用的对象(原始的Activity
)被销毁 - 或者一些内部Java对象维护对
AsyncTask
对象的引用,阻止其垃圾收集,有效地使AsyncTask
在后台完成
无论哪种方式,最好手动中止/暂停和重新启动/恢复AsyncTask
(或将其交给新的Activity
),或者使用Service
来代替。
这是怎么解决的?
它不。
当垃圾收集器确定所指对象是弱可达的时, WeakReference
被设置为null
。 活动暂停时不会发生这种情况,当活动被销毁时框架不一定会立即发生,框架会放弃所有对活动的引用。 如果GC没有运行,那么AsyncTask
完全可能完成,而它的WeakReference
仍然包含对死亡活动的引用。
不仅如此,这种方法并没有防止AsyncTask
用地占用CPU。
更好的方法是让Activity
保持对AsyncTask
的强引用,并在适当的拆卸生命周期方法中cancel(...)
。 AsyncTask
应该监视isCancelled()
,如果不再需要,就停止工作。
如果您希望AsyncTask
能够在configuration更改中生存(而不是其他forms的活动销毁),则可以将其托pipe在保留的片段中。