Android开发Android开发经验谈Android技术知识

Handler的内存泄漏

2020-04-10  本文已影响0人  大虾啊啊啊

1.1、简述

内存泄漏产生的原因通常就是因为生命周期长的类持有声明周期短的类的引用,当生命周期短的类销毁的时候自身的引用还被外界持有,导致无法GC,这样就会产生内存泄漏(也就是有一块内存已经不需要他工作了,但是却被占着不放。其他对象又不能用他,就会导致内存白白的浪费)。

1.2、Handler内存泄漏场景

我们先来看一个例子:

package com.example.myapplication;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.widget.TextView;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            if (msg.what == 200) {
                startActivity(new Intent(MainActivity.this, TestActivity.class));
            }
            return false;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread() {
            @Override
            public void run() {
                super.run();
                SystemClock.sleep(3000);
                handler.sendEmptyMessage(200);
            }
        }.start();

    }


}

以上是我们使用Handler的习惯用法,我们模拟一个耗时操作,在Thread的run方法中 休眠3S,类似我们的一个网络请求。我们启动MainActivity之后,按返回键马上退出,此时MainActivity的onDestroy方法被调用,MainActivity应该被销毁。当3S之后,我们的handler的消息还会发出去吗,并且执行handleMessage方法 startActivity(new Intent(MainActivity.this, TestActivity.class));,跳转到TestActivity。答案是肯定的。这个时候就出现了内存泄漏。因为MainActivity此时应该被销毁了,但是还能跳转到TestActivity这个类。那说明MainActivity还没有被真正销毁,Handler在这里我们使用了一个匿名内部类的方式实现。而匿名内部类会持有外部类的引用,也就是Handler持有了MainActivity 的引用。当MainActivity 退出的时候,本来应该被销毁。但是Handler还持有MainActivity 的引用,导致MainActivity 没有被真正释放掉。所以最终MainActivity 退出了3S之后,还能跳转到TestActivity。

1.3、Handler内存泄漏解决方案

针对以上Handler的内存泄漏,我们首先要了解两点:1.内部类会持有外部类的引用 2.匿名内部类会持有外部类的引用。那我们马上想到解决方案就是把上面的Thread Handler改成静态的内部类,然后把我们的MainActivity 传进去。但是这里又会出现一个新的问题,就是我们的类改成静态之后,那生命周期就比MainActivity 长,当MainActivity 被销毁了,Handler还会持有MainActivity 的引用。由于是强引用,我们无法手动GC。那么此时我们的解决要害就是,如何在MainActivity 销户之后手动的GC回收掉MainActivity 的引用,让外界不在持有。那么我们就可以联想到通过使用弱引用来实现。
请看以下代码:

package com.example.myapplication;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.widget.TextView;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private MyHandler handler;
    private MyThread thread;
    private WeakReference<MainActivity> weakReference;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        weakReference = new WeakReference<>(this);
        handler = new MyHandler(weakReference);
        thread = new MyThread(handler);
        thread.start();

    }


    private static class MyHandler extends Handler {
        //使用弱引用
        WeakReference<MainActivity> weakReference;

        public MyHandler(WeakReference<MainActivity> weakReference) {
            this.weakReference = weakReference;
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if (msg.what == 100) {
                //这里因为持有的是弱引用,在onDestroy 方法中 手动 清空weakReference,并且WeakReference设置为null
                // 所以 MainActivity 销毁的时候,MyHandler不在持有MainActivity的引用
                if (weakReference != null && weakReference.get() != null) {
                    Log.e(TAG, "handleMessage: startActivity" );
                    weakReference.get().startActivity(new Intent(weakReference.get(), TestActivity.class));
                }

            }

        }
    }

    private static class MyThread extends Thread {
        private Handler handler;

        public MyThread(Handler handler) {
            this.handler = handler;
        }

        @Override
        public void run() {
            super.run();
            SystemClock.sleep(3000);
            handler.sendEmptyMessage(100);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //手动GC
        //在onDestroy 方法中 手动 清空weakReference
        weakReference.clear();
        weakReference = null;
        System.gc();
    }
}

我们运行代码之后可以看到,当MainActivity 退出的时候,不会跳转到TestActivity。因为在MainActivity 的onDestroy方法中,我们手动把weakReference清空,而且我们还手动的GC。

1.4、Handler内存泄漏总结

Handler内存泄漏的主要原因是匿名内部类或者外部类持有了外部类的引用,当外部类销毁的时候,由于引用还被外界持有,导致内存没有得到释放。解决方案:静态内部类+弱引用的方式。

上一篇下一篇

猜你喜欢

热点阅读