Android 中的 WeakReference/AsyncTask 模式

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

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

我将把事件分解成几个步骤

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

上述解决方案是在“Pro Android 4”一书中推荐的AsyncTask中保留弱引用

WeakReference<Activity> weakActivity;

in method onPostExecute

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

这如何解决这种情况?

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

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

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


答案 1

这如何解决这种情况?

允许进行垃圾回收,因此您没有内存泄漏。WeakReferenceActivity

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

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

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

最初调用的先前版本会发生什么情况?AsyncTask

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

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

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

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

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

另请参阅此主题:AsyncTask在概念上真的存在缺陷,还是我只是错过了一些东西?

更新:

我创建了一个github项目,其中包含一个下载示例,用于另一个SO问题(如何修复android.os.NetworkOnMainThreadException?),但我认为它在这里也很重要。它具有额外的优点,即通过 通过以下方式返回结果,当您旋转设备时正在运行的下载将传送到重新启动的设备 。IntentServiceonActivityResultActivity


答案 2

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

我不会深入到Java的内存管理并直接回答你的问题:通过提供一种方法来了解它的父活动是否仍然有效,从而解决了这种情况。WeakReferenceAsyncTask

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

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

  • 要么 Java 停止线程并释放 ,因为唯一一个包含对它引用的对象(原始对象)被销毁AsyncTaskActivity
  • 或者一些内部Java对象维护对该对象的引用,阻止其垃圾回收,有效地将完成留在后台AsyncTaskAsyncTask

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


推荐