android中的WeakReference/AsyncTask模式

Muh*_*lib 55 java android weak-references android-asynctask

我对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
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Ste*_*vie 34

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

WeakReference允许Activity进行垃圾回收,所以你不要有内存泄漏.

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

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

取决于你的实现,但可能是 - 如果你不刻意做一些事情来重复下载不必要,比如在某处缓存结果.

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

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

在较新的Android中,我怀疑是否AsyncTask会被Activity启动它们而被杀死,但我怀疑的基础只是RoboSpice的内存泄漏演示(见下文)实际上并没有在我的JellyBean设备上泄漏.

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

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

如果你正在寻找一种在后台可靠地执行网络的方法,RoboSpice似乎很好(免责声明:我没有尝试过;我没有附属).游戏商店中有一个RoboSpice Motivations演示应用程序,通过演示可能出错的所有内容(包括WeakReference解决方法)来解释为什么要使用它AsyncTask.

另请参见此主题:AsyncTask在概念上是否真的存在缺陷,或者我只是遗漏了什么?

更新:

我创建了一个github项目,下载使用IntentService另一个SO问题的例子(如何修复android.os.NetworkOnMainThreadException?),但我认为这也与此相关.它具有额外的优势,通过返回结果onActivityResult,当您旋转设备时正在进行的下载将传递给重新启动的Activity.

  • 小更新:到目前为止,RxJava是一个在后台运行任务的好方法,离开主线程并在主线程上传递结果(这样你就可以更新UI等).另一种选择是使用EventBus, (2认同)

dom*_*som 7

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

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

方向更改本身不会自动重启AsyncTask.您必须使用已知机制(onCreate/ onDestroy,onSave/RestoreInstanceState)编写所需行为.

关于原版AsyncTask,我不是100%确定会发生以下哪种选择:

  • 要么Java停止线程并处理它AsyncTask,因为唯一对象持有它(原始Activity)被销毁
  • 或者某些内部Java对象维护对该AsyncTask对象的引用,阻止其垃圾收集,从而有效地AsyncTask在后台完成

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