如何能在AsyncTask的仍使用活动,如果用户已导航远离它? [英] How can an AsyncTask still use an Activity if the user has already navigated away from it?
问题描述
在Android上,你可以在一个单独的发做的工作
例如通过使用的Runnable
或的AsyncTask
。在这两种情况下,你可能需要通过重写做一些工作,工作完成后,例如 onPostExecute()
在的AsyncTask
。然而,用户可能离开或关闭应用程序工作时在后台正在做。
On Android you can do work in a separate Thread
for example by using a Runnable
or AsyncTask
. In both cases you might need to do some work after the work is done, for example by overriding onPostExecute()
in the AsyncTask
. However the user might navigate away or close the app while work is being done in the background.
我的问题是:如果用户导航离开或关闭应用程序,而我还是要在活动的引用会发生什么
用户只是在我的<$ C $关闭C>的AsyncTask ?
My question is: What happens if the user navigates away or closes the app while I still have a reference to the Activity
the user just closed in my AsyncTask
?
我的猜测是,它为用户导航离开应尽快销毁,但是当我测试它由于某种原因,我仍然可以调用上的活性的方法在设备上
即使它已经不见了!这里发生了什么?
My guess is that it should be destroyed as soon as the user navigates away, however when I test it out on a device for some reason I can still call methods on the Activity
even though it is already gone! What is going on here?
推荐答案
答案很简单:你刚刚发现
Simple answer: You have just discovered
只要喜欢的应用程序的某些部分的AsyncTask
仍持有到活动
它将<参考STRONG>不被破坏。它会坚持围绕直到的AsyncTask
已完成或发布了其以其他方式引用。这可以有一个像你的应用程序崩溃非常糟糕的后果,但最严重的后果是那些你没有注意到:你的应用程序可能会继续参照这应该被释放青睐活动
以前,每个用户做一次无论漏活动
设备上的内存可能会得到更多,更全,直到看似突然冒出的Android杀死你的应用程序的占用过多的内存。内存泄漏是一个最频繁,最严重的错误我在堆栈溢出
As long as some part of the app like an AsyncTask
still holds a reference to the Activity
it will not be destroyed. It will stick around until the AsyncTask
is done or releases its reference in some other way. This can have very bad consequences like your app crashing, but the worst consequences are the ones you don't notice: your app may keep reference to Activities
which should have been released ages ago and each time the user does whatever leaks the Activity
the memory on the device might get more and more full until seemingly out of nowhere Android kills your app for consuming too much memory. Memory leaks are the single most frequent and worst mistakes I see in Android questions on Stack Overflow
避免内存泄漏却非常简单:你的的AsyncTask
应绝不会有一个活动的参考
,服务
或任何其他UI组件。
Avoiding memory leaks however is very simple: Your AsyncTask
should never have a reference to an Activity
, Service
or any other UI component.
而是使用监听器模式,并始终使用的WeakReference
。永远不要持有强引用到外的AsyncTask
的东西。
Instead use the listener pattern and always use a WeakReference
. Never hold strong references to something outside the AsyncTask
.
一个正确实施的AsyncTask
,它使用了的ImageView
可能看起来像这样:
A correctly implemented AsyncTask
which uses an ImageView
could look like this:
public class ExampleTask extends AsyncTask<Void, Void, Bitmap> {
private final WeakReference<ImageView> mImageViewReference;
public ExampleTask(ImageView imageView) {
mImageViewReference = new WeakReference<>(imageView);
}
@Override
protected Bitmap doInBackground(Void... params) {
...
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
final ImageView imageView = mImageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
这完全什么的WeakReference
做说明。 在WeakReferences
允许对象
它们在引用进行垃圾回收。因此,在这个例子中,我们创建一个的WeakReference
到的ImageView
中的构造的AsyncTask
。然后在 onPostExecute()
可以称为10秒后来当的ImageView
不存在了,我们称之为获得()
在的WeakReference
来查看是否的ImageView
存在。只要的ImageView
按 get()返回
不是null,则的ImageView
尚未收集垃圾,因此我们可以使用它没有后顾之忧!如果在此期间用户退出应用程序,然后在的ImageView
将立即进行垃圾回收,如果的AsyncTask
完成一些时间后它看到的的ImageView
已经不见了。没有内存泄漏,没有任何问题。
This illustrates perfectly what a WeakReference
does. WeakReferences
allow for the Object
they are referencing to be garbage collected. So in this example We create a WeakReference
to an ImageView
in the constructor of the AsyncTask
. Then in onPostExecute()
which might be called 10 seconds later when the ImageView
does not exist anymore we call get()
on the WeakReference
to see if the ImageView
exists. As long as the ImageView
returned by get()
is not null then the ImageView
has not been garbage collected and we can therefore use it without worry! Should in the meantime the user quit the app then the ImageView
becomes eligible for garbage collection immediately and if the AsyncTask
finishes some time later it sees that the ImageView
is already gone. No memory leaks, no problems.
public class ExampleTask extends AsyncTask<Void, Void, Bitmap> {
public interface Listener {
void onResult(Bitmap image);
}
private final WeakReference<Listener> mListenerReference;
public ExampleTask(Listener listener) {
mListenerReference = new WeakReference<>(listener);
}
@Override
protected Bitmap doInBackground(Void... params) {
...
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
final Listener listener = mListenerReference.get();
if (listener != null) {
listener.onResult(bitmap);
}
}
}
这看起来很相似的,因为它实际上是相当类似的。您可以在活动使用这样
或片段
:
This looks quite similar because it actually is quite similar. You can use it like this in an Activity
or Fragment
:
public class ExampleActivty extends AppCompatActivity implements ExampleTask.Listener {
private ImageView mImageView;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
new ExampleTask(this).execute();
}
@Override
public void onResult(Bitmap image) {
mImageView.setImageBitmap(image);
}
}
或者你可以用这样的:
Or you can use it like this:
public class ExampleFragment extends Fragment {
private ImageView mImageView;
private final ExampleTask.Listener mListener = new ExampleTask.Listener() {
@Override
public void onResult(Bitmap image) {
mImageView.setImageBitmap(image);
}
};
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
new ExampleTask(mListener).execute();
}
...
}
的WeakReference
及其后果使用侦听器时
但是有你必须要知道的另一件事。只是有一个的WeakReference
来监听的结果。想象一下,你实现这样的监听器接口:
WeakReference
and its consequences when using a listener
However there is another thing you have to be aware of. A consequence of only having a WeakReference
to the listener. Imagine you implement the listener interface like this:
private static class ExampleListener implements ExampleTask.Listener {
private final ImageView mImageView;
private ExampleListener(ImageView imageView) {
mImageView = imageView;
}
@Override
public void onResult(Bitmap image) {
mImageView.setImageBitmap(image);
}
}
public void doSomething() {
final ExampleListener listener = new ExampleListener(someImageView);
new ExampleTask(listener).execute();
}
相当不寻常的方式做到这一点 - 我知道 - 但类似的东西可能的地方潜入你code而不让你知道,后果可能很难调试。你现在看到什么可能是错误与上面的例子?试着搞清楚,否则继续阅读下面的内容。
Quite an unusual way to do this - I know - but something similar might sneak into your code somewhere without you knowing it and the consequences can be difficult to debug. Have you noticed by now what might be wrong with the above example? Try figuring out, otherwise continue reading below.
问题很简单:你创建的实例 ExampleListener
,其中包含您参考的ImageView
。然后,你将其插入 ExampleTask
和启动任务。然后 DoSomething的()
方法完成,因此所有的局部变量符合垃圾收集。有留给你传递给 ExampleListener
实例没有强烈参考 ExampleTask
中,只有一个的WeakReference
。因此, ExampleListener
将被垃圾收集和 ExampleTask
结束的时候会发生什么。如果 ExampleTask
执行速度不够快,垃圾收集器可能没有收集到的 ExampleListener
实例还,所以它可能工作的一些时间或根本没有。和调试问题,这样可以是一个噩梦。所以这个故事的寓意是:永远知道你的坚强和弱引用,当对象变得符合垃圾收集。
The problem is simple: You create an instance of the ExampleListener
which contains your reference to the ImageView
. Then you pass it into the ExampleTask
and start the task. And then the doSomething()
method finishes, so all local variables become eligible for garbage collection. There is no strong reference left to the ExampleListener
instance you passed into the ExampleTask
, there is just a WeakReference
. So the ExampleListener
will be garbage collected and when the ExampleTask
finishes nothing will happen. If the ExampleTask
executes fast enough the garbage collector might not have collected the ExampleListener
instance yet, so it may work some of the time or not at all. And debugging issues like this can be a nightmare. So the moral of the story is: Always be aware of your strong and weak references and when objects become eligible for garbage collection.
另一件事,这可能是最内存泄漏我使用的方式是错误的嵌套类堆栈溢出人们看到的原因。请看下面的例子,并尝试找出什么在下面的例子中会导致内存泄漏:
Another thing which probably is the cause of most memory leaks I see on Stack Overflow people using nested classes in the wrong way. Look at the following example and try to spot what causes a memory leak in the following example:
public class ExampleActivty extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
final ImageView imageView = (ImageView) findViewById(R.id.image);
new ExampleTask(imageView).execute();
}
public class ExampleTask extends AsyncTask<Void, Void, Bitmap> {
private final WeakReference<ImageView> mListenerReference;
public ExampleTask(ImageView imageView) {
mListenerReference = new WeakReference<>(imageView);
}
@Override
protected Bitmap doInBackground(Void... params) {
...
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
final ImageView imageView = mListenerReference.get();
if (imageView != null) {
imageView.setImageAlpha(bitmap);
}
}
}
}
你看到了吗?这里是完全相同的问题另外一个例子,它只是看起来不同:
Do you see it? Here is another example with the exact same problem, it just looks different:
public class ExampleActivty extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
final ImageView imageView = (ImageView) findViewById(R.id.image);
final Thread thread = new Thread() {
@Override
public void run() {
...
final Bitmap image = doStuff();
imageView.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(image);
}
});
}
};
thread.start();
}
}
你有没有揣摩的问题是什么?我每天都看到有人不慎实现的东西像上面可能不知道他们在做什么错。问题是Java的运行方式基于Java的基本特征的后果 - 没有任何借口,谁执行上面的一样东西的人要么是喝醉了或者不知道关于Java什么。让我们简化问题:
Have you figured out what the problem is? I daily see people carelessly implementing stuff like above probably without knowing what they are doing wrong. The problem is a consequence of how Java works based on a fundamental feature of Java - there is no excuse, people who implement things like above are either drunk or don't know anything about Java. Let's simplify the problem:
假设你有一个嵌套类是这样的:
Imagine you have a nested class like this:
public class A {
private String mSomeText;
public class B {
public void doIt() {
System.out.println(mSomeText);
}
}
}
当你做,你可以从类内部访问类 A
成员 B
。那怎么的doIt()
可以打印 mSomeText
,它可以访问的所有成员
甚至私人的。结果
你可以这样做的原因是,如果你窝类一样,Java的隐式创建 B
的内部 A
参考。正是因为这一点参考,没有别的,你有 B
内获得 A
中的所有成员。然而,在这再次提出了一个问题,内存泄漏的情况下,如果你不知道你在做什么。考虑的第一个例子(我将去除所有不从示例重要的部分):
When you do that you can access members of the class A
from inside the class B
. That's how doIt()
can print mSomeText
, it has access to all the members of A
even private ones.
The reason you can do that is that if you nest classes like that Java implicitly creates a reference to A
inside of B
. It is because of that reference and nothing else that you have access to all members of A
inside of B
. However in the context of memory leaks that again poses a problem if you don't know what you are doing. Consider the first example (I'll strip all the parts that don't matter from the example):
public class ExampleActivty extends AppCompatActivity {
public class ExampleTask extends AsyncTask<Void, Void, Bitmap> {
...
}
}
因此,我们有一个的AsyncTask
为活动的内部嵌套类
。由于嵌套类也不是一成不变的,我们可以访问内部的 ExampleTask
的为ExampleActivity
的成员。不要紧,在这里, ExampleTask
不实际访问从活动的任何成员
,因为它是一个非Static Nested Class和Java的暗中创建的活动的参考
中的 ExampleTask
等看似没有明显的原因,我们有一个内存泄漏。我们如何解决这个问题?其实很简单。我们只需要添加一个字,那就是静态的:
So we have an AsyncTask
as a nested class inside an Activity
. Since the nested class is not static we can access members of the ExampleActivity
inside the ExampleTask
. It doesn't matter here that ExampleTask
doesn't actually access any members from the Activity
, since it is a non static nested class Java implicitly creates a reference to the Activity
inside the ExampleTask
and so with seemingly no visible cause we have a memory leak. How can we fix this? Very simple actually. We just need to add one word and that is static:
public class ExampleActivty extends AppCompatActivity {
public static class ExampleTask extends AsyncTask<Void, Void, Bitmap> {
...
}
}
就这一个一个简单的嵌套类缺少关键字是内存泄漏,完全罚款code之间的区别。真正尝试了解这里的问题,因为它是在Java的是如何工作的核心和理解,这是至关重要的。
Just this one missing keyword on a simple nested class is the difference between a memory leak and completely fine code. Really try to understand the issue here, because it is at the core of how Java works and understanding this is crucial.
和作为与发的另一个例子
?完全相同的问题,匿名类一样,也只有非静态内部类,并立即内存泄漏。然而,它实际上是一百万次更糟。无论从哪个角度,你看它的发
的例子是太可怕了code。不惜一切代价避免。
And as for the other example with the Thread
? The exactly same issue, anonymous classes like that are also just non static nested classes and immediately a memory leak. However it is actually a million times worse. From every angle you look at it that Thread
example is just terrible code. Avoid at all costs.
所以,我希望这些例子帮助你理解问题以及如何编写没有内存泄漏的code。如果您有任何其他问题随时问。
So I hope these example helped you understand the problem and how to write code free of memory leaks. If you have any other questions feel free to ask.
这篇关于如何能在AsyncTask的仍使用活动,如果用户已导航远离它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!