很多场景下需要我们在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的回收,并不会造成多么严重的内存泄露,因此重中之重还是要提高编码质量。
欢迎 扫一扫 下方二维码
关注我的公众号 阅读更多文章