如何能在AsyncTask的仍使用活动,如果用户已导航远离它? [英] How can an AsyncTask still use an Activity if the user has already navigated away from it?

查看:102
本文介绍了如何能在AsyncTask的仍使用活动,如果用户已导航远离它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在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 已完成或发布了其以其他方式引用。这可以有一个像你的应用程序崩溃非常糟糕的后果,但最严重的后果是那些你没有注意到:你的应用程序可能会继续参照这应该被释放青睐活动以前,每个用户做一次无论漏活动设备上的内存可能会得到更多,更全,直到看似突然冒出的Andr​​oid杀死你的应用程序的占用过多的内存。内存泄漏是一个最频繁,最严重的错误我在堆栈溢出

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆