Java android 中的 WeakReference/AsyncTask 模式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18297378/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:14:03  来源:igfitidea点击:

WeakReference/AsyncTask pattern in android

javaandroidandroid-asynctaskweak-references

提问by Muhammad Ahmed AbuTalib

I have a question regarding this simple frequently occurring situation in android .

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

We have a main activity , we invoke an AsyncTask alongwith the reference of the mainactivity , so that that the AsyncTask can update the views on the MainActivity.

我们有一个 main Activity ,我们调用一个 AsyncTask 以及 mainactivity 的引用,以便 AsyncTask 可以更新 MainActivity 上的视图。

I will break down the event into steps

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

  • MainActivity creates an AyncTask , passes its reference to it .
  • AysncTask , starts it's work , downloading ten files for example
  • The user changed the orientation of the device. This results in an orphan pointer in the AsyncTask
  • When the AsyncTask completes , and tries to access the activity to update the status , it crashes , because of the null pointer .
  • MainActivity 创建一个 AyncTask,将其引用传递给它。
  • AysncTask ,开始工作,例如下载十个文件
  • 用户更改了设备的方向。这会导致 AsyncTask 中出现孤立指针
  • 当 AsyncTask 完成并尝试访问活动以更新状态时,由于空指针而崩溃。

The solution for the above is to keep a WeakReference in the AsyncTask as recommended by the book "Pro Android 4"

上面的解决方案是按照“Pro Android 4”一书的建议在AsyncTask中保留一个WeakReference

WeakReference<Activity> weakActivity;

in method onPostExecute

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

How does this resolve the situation ?

这如何解决这种情况?

My question it , if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.

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

What would happen to the previous AsyncTask that was initially invoked ?

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

Thank you , and I apologize for the length of the question .

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

采纳答案by Stevie

How does this resolve the situation ?

这如何解决这种情况?

The WeakReferenceallows the Activityto be garbage collected, so you don't have a memory leak.

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

A null reference means that the AsyncTaskcannotblindly try to update a user-interface that is no longer attached, which would throw exceptions (e.g. view not attached to window manager). Of course you have to check for null to avoid NPE.

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

if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.

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

Depends on your implementation, but probably yes - if you don't deliberately do something to make a repeat download unnecessary, such as caching the results somewhere.

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

What would happen to the previous AsyncTaskthat was initially invoked ?

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

In earlier versions of Android it would run to completion, downloading all of the files only to throw them away (or perhaps cache them, depending on your implementation).

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

In newer Android's I am suspicious that AsyncTask's are being killed along with the Activitythat started them, but my basis for suspicion is only that the memory-leak demo's for RoboSpice(see below) do not actually leak on my JellyBean devices.

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

If I may offer some advice: AsyncTaskis not suitable for performing potentially long running tasks such as networking.

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

IntentServiceis a better (and still relatively simple) approach, if a single worker thread is acceptable to you. Use a (local) Serviceif you want control over the thread-pool - and be careful not to do work on the main thread!

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

RoboSpiceseems good if you are looking for a way to reliably perform networking in the background (disclaimer: I have not tried it; I am not affiliated). There is a RoboSpice Motivations demo appin the play store which explains whyyou should use it by demo-ing all the things that can go wrong with AsyncTask- including the WeakReference workaround.

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

See also this thread: Is AsyncTask really conceptually flawed or am I just missing something?

另请参阅此线程:AsyncTask 真的在概念上有缺陷还是我只是遗漏了什么?

Update:

更新:

I created a github projectwith an example of downloading using IntentServicefor another SO question (How to fix android.os.NetworkOnMainThreadException?), but it is also relevant here, I think. It has the added advantage that, by returning the result via onActivityResult, a download that is in flight when you rotate the device will deliver to the restarted Activity.

我创建了一个github 项目,其中包含下载IntentService用于另一个 SO 问题的示例(如何修复 android.os.NetworkOnMainThreadException?),但我认为它在这里也很相关。它还有一个额外的优势,即通过通过 返回结果onActivityResult,当您旋转设备时正在进行的下载将交付给重新启动的Activity.

回答by domsom

The WeakReferenceclass basically just prevents the JRE to increase the reference counter for the given instance.

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

I won't go into Java's memory management and answer your question directly: The WeakReferenceresolves the situation by providing the AsyncTaska way to learn if its parent activity is still valid.

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

The orientation change itself will not automatically restart the AsyncTask. You have to code the desired behavior with the known mechanisms (onCreate/onDestroy, onSave/RestoreInstanceState).

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

Concerning the original AsyncTask, I'm not 100 % sure which of these options will happen:

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

  • Either Java stops the thread and disposes the AsyncTask, because the only object holding a reference to it (the original Activity) is destroyed
  • Or some internal Java object maintains a reference to the AsyncTaskobject, blocking its garbage collection, effectively leaving the AsyncTaskto finish in the background
  • 要么 Java 停止线程并处理AsyncTask,因为持有对它的引用的唯一对象(原始Activity)已被销毁
  • 或者一些内部的 Java 对象维护对对象的引用AsyncTask,阻止其垃圾收集,有效地让该对象AsyncTask在后台完成

Either way, it would be good practice to abort/pause and restart/resume the AsyncTaskmanually (or handing it over to the new Activity), or use a Serviceinstead.

无论哪种方式,AsyncTask手动中止/暂停和重新启动/恢复(或将其交给新的Activity),或改用 a都是很好的做法Service

回答by Kevin Krumwiede

How does this resolve the situation ?

这如何解决这种情况?

It doesn't.

它没有。

The referent of a WeakReferenceis set to nullwhen the garbage collector determines that the referent is weakly reachable. This does not happen when an activity is paused, and does not necessarily happen immediately when the activity is destroyed and the framework discards all references to it. If the GC has not run, it is entirely possible for the AsyncTaskto complete while its WeakReferencestill contains a reference to a dead activity.

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

Not only that, but this approach does nothing to prevent the AsyncTaskfrom uselessly consuming CPU.

不仅如此,这种方法也无法阻止AsyncTask无用的 CPU 消耗。

A better approach is to have the Activitymaintain a strong reference to the AsyncTaskand cancel(...)it in the appropriate teardown lifecycle method. The AsyncTaskshould monitor isCancelled()and stop working if it is no longer needed.

更好的方法是在适当的拆卸生命周期方法中Activity维护对AsyncTaskcancel(...)it的强引用。如果不再需要,AsyncTask应该监视isCancelled()并停止工作。

If you want an AsyncTaskto survive across configuration changes (but notother forms of activity destruction) you can host it in a retained fragment.

如果您希望AsyncTask在配置更改(但不是其他形式的活动破坏)中继续存在,您可以将其托管在保留片段中。