简体   繁体   English

为什么异步网络调用的回调方法在活动结束后不会导致内存泄漏?

[英]Why callback method for asynchronous network call can't cause memory leak when activity finished?

We know that anonymous inner class may cause memory leak. 我们知道匿名内部类可能会导致内存泄漏。 But Why it does't work when asynchronous network call. 但是为什么它在异步网络调用时不起作用。
For example: 例如:

OkHttpClient client = new OkHttpClient();

 Request request = new Request.Builder()
                .get()
                .url(url)
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                // String str = response.body().string();
                // do sth to our View, but those views may be null when activity finished
                }

            }
        });

We will change our view's states when callback method call, but those views always be null when activity finished. 我们将在回调方法调用时更改视图的状态,但这些视图在活动完成时始终为null。 Why this anonymous inner class instance for callback can't cause activity leak. 为什么这个用于回调的匿名内部类实例不会导致活动泄漏。

why this anonymous inner class instance for callback can't cause activity leak 为什么这个用于回调的匿名内部类实例不会导致活动泄漏

I'm assuming that what you mean here is that it doesn't cause a memory leak, but it most certainly could, given that the scope in which you're instantiating the anonymous Callback is an Activity . 我假设你在这里的意思是它不会导致内存泄漏,但它肯定可以,因为你实例化匿名Callback是一个Activity

If you instantiate an inner class within an Android Activity and then pass a reference to this instance to some other component, as long as this component is reachable, so will be the instance of the inner class. 如果在Android Activity实例化一个内部类,然后将对该实例的引用传递给其他组件,只要该组件可以访问,那么内部类的实例也是如此。 For instance, consider this: 例如,考虑一下:

class MemorySink {

    static private List<Callback> callbacks = new ArrayList<>();

    public static void doSomething(Callback callback){
        callbacks.add(callback);
    }
}

If you created instances of Callback s from some activities and pass them to doSomething(callback) , when one of the Activity is destroyed the system will not use that instance anymore, it is expected that the garbage collector will release that instance. 如果您从某些活动创建了Callback实例并将它们传递给doSomething(callback) ,当其中一个Activity被销毁时,系统将不再使用该实例,预计垃圾收集器将释放该实例。 But, if MemorySink here has a reference to a Callback that has a reference to that Activity , the instance of that Activity will stay in memory even after being destroyed. 但是,如果MemorySink这里有一个参考Callback是有一个参考Activity ,该实例Activity甚至会遭到破坏后保留在内存中。 Bam, memory leak. Bam,内存泄漏。

So you say that your sample is not causing the memory leak, I will first suggest you to try out MemorySink , create a simple Activity "MainActivity" with 2 buttons and possibly some images to increase the memory footprint. 所以你说你的样本没有引起内存泄漏,我首先建议你试试MemorySink ,创建一个简单的Activity “MainActivity”,带有2个按钮,可能还有一些图像来增加内存占用。 In onCreate set a listener on the first button this way: onCreate ,在第一个按钮上设置一个监听器:

    findViewById(R.id.firstButton).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startActivity(new Intent(MainActivity.this, MainActivity.class));
            MemorySink.doSomething(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {

                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {

                }
            });
            finish();
        }
    });

You've just created a memory leak using Callback . 您刚刚使用Callback创建了内存泄漏。 Every time you click on the button in MainActivity that instance of MainActivity will be destroyed and a new one will be created. 每当你在点击该按钮MainActivity该实例MainActivity将被销毁,新的一个将被创建。 But, the old instance of MainActivity will be retained in memory. 但是, MainActivity的旧实例将保留在内存中。 I invite you to click many times the button and then dump the memory (with Android Profiler in Android Studio), or use LeakCanary. 我邀请您多次单击该按钮,然后转储内存(在Android Studio中使用Android Profiler),或使用LeakCanary。

So, we've created a memory leak with Callback , same class from OP. 因此,我们使用Callback创建了一个内存泄漏,与OP相同。 Now, let's add this method to MemorySink : 现在,让我们将此方法添加到MemorySink

public static void releaseAll() {
    callbacks.clear();
}

And call it from another button on MainActivity . 并从MainActivity上的另一个按钮调用它。 If you press the first button many times (better if you have images in MainActivity ) you will see memory usages going up, even if you manually trigger garbage collection (Android Profile). 如果您多次按下第一个按钮(如果您在MainActivity有图像,则会更好),即使手动触发垃圾收集(Android配置文件),您也会看到内存使用率上升。 Then you click this second button, all reference to Callback are released, trigger garbage collection, and memory goes down. 然后单击第二个按钮,释放对Callback所有引用,触发垃圾回收,内存中断。 No more memory leak. 没有更多的内存泄漏。

So the problem is not if Callback can or cannot create a memory leak, it most certainly can. 所以问题不是如果Callback能够或不能创建内存泄漏,它肯定可以。 The problem is where are you passing that Callback . 问题是你在哪里传递Callback In that case OkHttpClient is not creating a memory leak, so you say, but it is not guaranteed at all that this will always happen. 在那种情况下, OkHttpClient不会产生内存泄漏,所以你说,但总是不能保证会发生这种情况。 In that case you would need to be sure about the implementation of OkHttpClient to say that it won't generate a memory leak. 在这种情况下,您需要确保OkHttpClient的实现,以说它不会产生内存泄漏。

My suggestion is to always assume that the memory leak can happen if you are passing a reference to an Activity to some external class. 我的建议是始终假设如果您将对Activity的引用传递给某个外部类,则可能发生内存泄漏。

Yes you are correct, that's why you can add a 是的,你是对的,这就是为什么你可以添加一个

 if (response.isSuccessful()) {
                // String str = response.body().string();
                if(view != null){
                //do sth to view
                }
 }

A non-static inner class has a strong reference to the instance of the outer class. 非静态内部类具有对外部类的实例的强引用。 On the other hand a static inner class has no strong reference to the instance of the outer class. 另一方面,静态内部类没有对外部类的实例的强引用。 It uses WeakReference to reference outer class. 它使用WeakReference引用外部类。 you can learn more here 你可以在这里了解更多

client.newCall(request).enqueue(new Callback() {...})

Here you are passing a callback object to retrofit. 在这里,您将传递一个回调对象进行改造。 By this, you are telling retrofit to use it in it's process to call-back to you when the call has finished. 通过这种方式,您正在告诉改造在它的过程中使用它来在呼叫结束时回拨给您。

That process is not in the current activity and it is in a separate thread and context (conceptually, not android's context). 该进程不在当前活动中,它位于单独的线程和上下文中(概念上,不是android的上下文)。 After your activity is destroyed by something (like rotation), the retrofit service is probably still alive and is holding a reference to this object and your activity causes a memory leak (it cannot be cleared from memory, and thus pollutes the memory) because your activity's callback object is needed by the retrofit. 在您的活动被某些东西(如旋转)破坏之后,改装服务可能仍然存在,并且持有对此对象的引用,并且您的活动导致内存泄漏(无法从内存中清除,从而污染内存),因为改造需要活动的回调对象。

If you wanted to fix this memory leak, you would need to cancel the call and remove the callback in activity's onStop , but that way you would lose the call on rotation. 如果你想修复这个内存泄漏,你需要取消调用并删除activity onStop中的回调,但这样你就会失去旋转调用。

The better thing to do, which is highly recommended, is that you don't do the asynchronous stuff inside the activity itself, and do it in a separate object (like Presenter in a MVP pattern or ViewModel in MVVM) that has a different lifecycle than the activity (a lifecycle that does not get destroyed by rotation or ...). 强烈建议做的更好的事情是,你不要在活动本身内部执行异步操作,而是在具有不同生命周期的单独对象(如MVP模式中的Presenter或MVVM中的ViewModel)中执行此操作。而不是活动(不会被轮换或......破坏的生命周期)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如果一个Activity实现了一个回调,并且该引用传递给另一个线程中的网络调用并且旋转了屏幕,这是否是内存泄漏? - Is it a memory leak if an Activity implements a callback and that reference is passed for a network call in another thread and the screen is rotated? 当此方法引发异常时,会导致内存泄漏吗? - Will this method cause a memory leak when it throws an exception? 为什么该程序会导致内存泄漏? - Why this program can cause memory Leak? 为什么在Android中使用Activity的简单示例会导致内存泄漏? - Why does simple example using Activity in Android cause a memory leak? 特定活动完成后如何调用方法 - How to call a method when a particular activity is finished 为什么不在Java中实现equals方法导致内存泄漏 - why does not implementing equals method in Java cause memory leak 同步异步回调方法调用 - Synchronize the asynchronous callback method call 方法中列出的分配是否会导致内存泄漏 - Would an assignment listed in the method cause a memory leak 方法范围内的Texture声明会导致内存泄漏吗? - will Texture declaration in method scope cause memory leak? 为什么此功能会导致内存泄漏? - Why does this function cause a memory leak?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM