Handler内存泄漏原因及解决方案

一、Handler造成内存泄露的原因

在Activity中,将Handler声明成非静态内部类或匿名内部类,这样Handle默认持有外部类Activity的引用。如果Activity在销毁时,Handler还有未执行完或者正在执行的Message,而Handler又持有Activity的引用,导致GC无法回收Activity,导致内存泄漏。如以下两种情形可能导致内存泄漏

1、在Activity内将Handler声明成匿名内部类

   //匿名内部类
   private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };
   new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            //大量的操作,activity要销毁时还没结束
        }
    },1000);

2、在Activity内将Handler声明成非静态内部类:

   //非静态内部类
   private class MyHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    }

    private MyHandler mHandler = new MyHandler();

二、解决方案

1、静态内部类 + 弱引用

   private static class MyHandler extends Handler {
        //弱引用,在垃圾回收时,activity可被回收
        private WeakReference<MainActivity> mWeakReference;

        public MyHandler(MainActivity activity) {
            mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    }

2.在Activity销毁时,清空Handler中未执行或正在执行的Callback以及Message:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //清空handler管道和队列
        mHandler.removeCallbacksAndMessages(null);
    }

3:非静态内部类 + 弱引用,在activity要回收时清除引用(麻烦,不推荐)

    private class MyHandler extends Handler {
        //弱引用,在垃圾回收时,activity可被回收
        private WeakReference<MainActivity> mWeakReference;

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

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    }
    
    private MyHandler mHandler = new MyHandler();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //手动清除应用
        mHandler.mWeakReference.clear();
    }