Android handle内存泄漏

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_15345551/article/details/100185513

一,什么是内存泄漏

首先我们要有堆内存的概念,堆内存是用来存放对象的,堆内存和栈内存的概念可以参考这篇文章https://www.cnblogs.com/liyonghua/p/8805017.html

jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身,几乎所有的对象实例和数组都在堆中分配。当对象不被引用时,会被gc回收,释放占用的堆内存。如果不再用到的对象,被错误引用,而无法被回收,这就发生了“内存泄漏”

二,handle内存泄漏的原因

非内部类默认会持有外部类的引用,这样当Activity被销毁时,由于被匿名handler对象所持有而不能被释放,Activity所占用的内存就会泄露。

public class HandleTestActivity extends AppCompatActivity {

    private TextView tv;
    private int id = 0;
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.e("HandleTestActivity", "handleMessage");
            tv.setText(msg.arg1 + "");
            Toast.makeText(HandleTestActivity.this, msg.arg1 + " handleMessage", Toast.LENGTH_LONG).show();
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e("HandleTestActivity", "onCreate");
        setContentView(R.layout.activity_handle_test);
        tv = findViewById(R.id.tv);
        Message msg = handler.obtainMessage();
        ++id;
        msg.arg1 = id;
        handler.sendMessageDelayed(msg, 2000);
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.e("HandleTestActivity", "onPause");
        Message msg = handler.obtainMessage();
        ++id;
        msg.arg1 = id;
        handler.sendMessageDelayed(msg, 100000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("HandleTestActivity", "onDestroy");
        //我们可以在Activity onDestroy()时调用handler.removeCallbacksAndMessages(null),
        // 这样就把queue里所有的message都remove掉了,之前说过message被message pool回收掉会reset,
        // 因此不会再引用handler,这条引用链就断掉了。
//        handler.removeCallbacksAndMessages(null);
    }

    /**
     * 当垃圾回收确定不再有对对象的引用时,由垃圾回收器对对象调用。
     * 子类重写@code finalize方法以释放系统资源或执行其他清理。
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        Log.e("HandleTestActivity", "finalize");
    }
}

对于非静态的内部类,内部类的创建依赖外部类的实例对象,在没有外部类实例之前是无法创建内部类的。所以非内部类默认会持有外部类的引用,可以查看build后的class文件,文件路径 app\build\intermediates\javac\

文件展开如下,HandleTestActivity$1 表示HandleTestActivity为外部类,1代表匿名内部类,构造方法传了HandleTestActivity的实例。非静态的内部类也是类似的!

class HandleTestActivity$1 extends Handler {
    HandleTestActivity$1(HandleTestActivity this$0) {
        this.this$0 = this$0;
    }

    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Log.e("HandleTestActivity", "handleMessage");
        HandleTestActivity.access$000(this.this$0).setText(msg.arg1 + "");
        Toast.makeText(this.this$0, msg.arg1 + " handleMessage", 1).show();
    }
}

当启动HandleTestActivity后再finish,可以通过Android studio的profiler工具查看引用是否被释放

handler实例引用了Activity,handler又被其messsage.target所引用,message放入message queue中,message queue生命周期结束前,message queue都间接引用了activity,导致内存泄漏!

也有些文章提到,可以在Activity onDestroy()时调用handler.removeCallbacksAndMessages(null),这样确实message queue对activity的引用链没有了,如下图,但是handle对activity的引用链仍然存在

可以看到handle仍然引用着当前的this

三。解决方法

最好的解决方式就是使用静态内部类解决

public class HandleTest2Activity extends AppCompatActivity {

    private TextView tv;
    private int id = 0;
    private Handler handler = new Myhandle(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e("HandleTest1Activity", "onCreate");
        setContentView(R.layout.activity_handle_test2);
        tv = findViewById(R.id.tv);
        Message msg = handler.obtainMessage();
        ++id;
        msg.arg1 = id;
        handler.sendMessageDelayed(msg, 2000);
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.e("HandleTest1Activity", "onPause");
        Message msg = handler.obtainMessage();
        ++id;
        msg.arg1 = id;
        handler.sendMessageDelayed(msg, 40000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("HandleTest1Activity", "onDestroy");
//        handler.removeCallbacksAndMessages(null);
    }

    /**
     * 当垃圾回收确定不再有对对象的引用时,由垃圾回收器对对象调用。
     * 子类重写@code finalize方法以释放系统资源或执行其他清理。
     *
     * @throws Throwable
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        Log.e("HandleTest1Activity", "finalize");
    }

    private static class Myhandle extends Handler {
        private WeakReference<Context> reference;//弱引用
        public Myhandle(Context context) {
            reference = new WeakReference<>(context);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Context context = reference.get();
            Log.e("Myhandle", "handleMessage context=" + context);
            if (null != context){
                Toast.makeText(context, msg.arg1 + " handleMessage", Toast.LENGTH_LONG).show();
            }
        }
    }
}

静态内部类编译好的class文件,对照HandleTestActivity$1,构造方法没有传入外部类的对象

class HandleTest2Activity$Myhandle extends Handler {
    private WeakReference<Context> reference;

    public HandleTest2Activity$Myhandle(Context context) {
        this.reference = new WeakReference(context);
    }

    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Context context = (Context)this.reference.get();
        Log.e("Myhandle", "handleMessage context=" + context);
        if (null != context) {
            Toast.makeText(context, msg.arg1 + " handleMessage", 1).show();
        }

    }
}

此时看运行完的结果,此时已经没有handle对activity的引用了

四,leakcanary

内存泄漏也可以使用leakcanary分析

源码地址 https://dev.tencent.com/u/kuangxuefeng/p/AndroidTestDemo/git

猜你喜欢

转载自blog.csdn.net/qq_15345551/article/details/100185513
今日推荐