Handler引起的内存泄露

                                           Handler引起的内存泄露

很多场景下需要我们在Activity中使用Handler来将更新UI的操作切换到主线程,这也是Handler最常用的使用场景,Handler使用虽然很简单,但是这句话,应该很多人都不陌生“This Handler class should be static or leaks might occur”,那么为什么会有这样的内存泄露警告呢?

1. 在Java中,非静态内部类和匿名内部类都会隐式地持有其外部类的引用。静态内部类不会持有外部类的应用。

2.每个Handler都需要一个Looper来处理消息,但是线程是默认没有Looper的,如果需要使用Handler,就需要为线程创建Looper。在应用的主线程(ActivityThread)创建时会自动初始化一个Looper,这也是为什么主线程中可以默认使用Handler的原因。因此主线程中的Looper生命周期是和应用的一样长。

3.发送的消息中包含有Handler实例的引用,只有消息被处理后,handler的引用才会释放,如果Activity已经销毁,但是Looper中还有消息没有处理完。handler就不能释放,如果handler不是Activity的静态内部类,那么handler就会持有Activity的引用,导致该Activity不能正常回收,这样就造成了内存泄露。

那么如何解决呢?

1. 当Activity销毁时,handler中的未处理消息通常也没有必要处理了,因此可以将这些消息都取消掉。如下:

@Override
    protected void onDestroy(){
        super.onDestroy();
        Log.d("SecondActivity", "onDestroy");
        testHandler.removeCallbacksAndMessages(null);
    }

2. 将Handler类声明为静态内部类,同时持有Activity的弱引用,这样就不会影响Activity的回收了,示例如下

private static class TestHandler extends Handler {
        private final WeakReference<SecondActivity> secondActivityWeakReference;
        private TestHandler(SecondActivity secondActivity) {
            secondActivityWeakReference = new WeakReference<SecondActivity>(secondActivity);
        }
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 111:
                    SecondActivity secondActivity = secondActivityWeakReference.get();
                    Log.d("SecondActivity", "handleMessage");
                    Log.d("SecondActivity", "secondActivity : " + secondActivity);
                    //注意判空,Activity销毁后,经过足够长的时间后弱引用实例会被系统回收
                    //这个时间要看GC的时机了
                    if (secondActivity != null) {
                        secondActivity.showToast();
                        secondActivity.testHandler.sendEmptyMessageDelayed(111, 10 * 1000);
                    }
                    break;
                default:
                    break;
            }
        }
    }

下面在放一个会造成内存泄露的示例,用来比较:

private class SecondHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 111:
                    Log.d("SecondActivity", "handleMessage");
                    Log.d("SecondActivity", "secondActivity : " + SecondActivity.this);
                    //Handler会一直持有Activity的引用,导致Activity不能回收
                    if (SecondActivity.this != null) {
                        showToast();
                        //方便测试,循环执行,可能在实际使用时,并不会有这么多的消息
                        testHandler.sendEmptyMessageDelayed(111, 10 * 1000);
                    }
                    break;
                default:
                    break;
            }
        }
    }

3. 其实所谓的内存泄露都是由于编程的不规范导致的,在Activity中使用Handler,无非就是更新UI,也不会在Handler中执行耗时操作,或者消息delay消息时间非常的长,或者像我测试示例中Handler死循环,如果不出现这些不规范的操作,最多也就是延迟系统对activity的回收,并不会造成多么严重的内存泄露,因此重中之重还是要提高编码质量。

猜你喜欢

转载自blog.csdn.net/u012216131/article/details/82770273