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

Cyr*_*rus 12 java android jvm okhttp

我们知道匿名内部类可能会导致内存泄漏.但是为什么它在异步网络调用时不起作用.
例如:

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
                }

            }
        });
Run Code Online (Sandbox Code Playgroud)

我们将在回调方法调用时更改视图的状态,但这些视图在活动完成时始终为null.为什么这个用于回调的匿名内部类实例不会导致活动泄漏.

lel*_*man 8

为什么这个用于回调的匿名内部类实例不会导致活动泄漏

我假设你在这里的意思是它不会导致内存泄漏,但它肯定可以,因为你实例化匿名的范围Callback是一个Activity.

如果在Android中实例化一个内部类Activity,然后将对该实例的引用传递给其他组件,只要该组件可以访问,那么内部类的实例也是如此.例如,考虑一下:

class MemorySink {

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

    public static void doSomething(Callback callback){
        callbacks.add(callback);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您Callback从某些活动创建了s 实例并将其传递给doSomething(callback),当其中一个Activity被销毁时,系统将不再使用该实例,预计垃圾收集器将释放该实例.但是,如果MemorySink这里有一个引用Callback它的引用,那么它Activity的实例Activity即使在被销毁之后也会留在内存中.Bam,内存泄漏.

所以你说你的样本没有引起内存泄漏,我首先建议你试试MemorySink,创建一个简单的Activity"MainActivity",带有2个按钮,可能还有一些图像来增加内存占用.在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();
        }
    });
Run Code Online (Sandbox Code Playgroud)

您刚刚使用创建了内存泄漏Callback.每次单击MainActivity该实例中的按钮都MainActivity将被销毁,并且将创建一个新按钮.但是,旧的实例MainActivity将保留在内存中.我邀请您多次单击该按钮,然后转储内存(在Android Studio中使用Android Profiler),或使用LeakCanary.

所以,我们用CallbackOP 创建了一个内存泄漏.现在,让我们将此方法添加到MemorySink:

public static void releaseAll() {
    callbacks.clear();
}
Run Code Online (Sandbox Code Playgroud)

然后从另一个按钮调用它MainActivity.如果您多次按第一个按钮(如果您有图像,则更好MainActivity),即使您手动触发垃圾收集(Android配置文件),您也会看到内存使用率上升.然后单击第二个按钮,Callback释放所有引用,触发垃圾回收,内存中断.没有更多的内存泄漏.

所以问题不是如果Callback能够或不能创建内存泄漏,它肯定可以.问题是你在哪里通过Callback.在这种情况下OkHttpClient,不会产生内存泄漏,所以你说,但总是不能保证会发生这种情况.在这种情况下,您需要确保实现,OkHttpClient以说它不会产生内存泄漏.

我的建议是始终假设如果您将引用传递Activity给某个外部类,则可能发生内存泄漏.