1.handler容易引发内存泄露
面试时经常被问到的一个问题:如何避免Handler造成内存泄漏?
内存泄漏:当不再需要某个实例后,这个对象却仍然被引用,不能被垃圾回收,这个情况就叫做内存泄露。
Handler使用不当很容易引起内存泄露,比如:
public class MainActivity extends Activity {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
}
这样使用handler的时候,其实是将handler定义为了匿名内部类。造成这段代码可能导致内存泄露。Android Lint会提示以下信息:
In Android, Handler classes should be static or leaks might occur.
那Handler引发内存泄露的原因是什么呢?
①当一个Android应用程序启动的时候,Android框架为这个程序的主线程创建了一个Looper对象,用于处理Handler发送过来的Message。Looper会创建对应的消息队列messagequeue,然后不断循环的处理其中的message。所有的应用程序框架的事件(比如Activity生命周期的调用、按钮的点击等)都被封装在这个Message对象里,然后被加入到Looper对应的Messagequeue中,最后一个一个的被处理。注意,主线程的Looper在整个应用程序的生命周期中一直存在。
②在主线程中实例化一个Handler对象的时候,就和它关联了主线程Looper的消息队列。消息被发送到消息队列时,入队列之前会把handler赋值给message的target属性,这样Message就持有了这个Handler对象的引用。(也就是说,只要没有处理到这个Message,Handler就一直在队列中被引用)
③在Java中,非静态内部类和匿名内部类都隐式的保持了对外部类的引用。但是静态内部类不会有这个引用。
即Message持有Handler的引用;Handler是内部类,它持有外部类Activity的引用;只要这个Message不被处理,那外部类Activity就永远不会被释放,哪怕是它已经不再被需要时调用了它的onDestroy方法,也无法释放,这样就引发了内存泄漏。
探究Handler导致内存泄露的真正原因:
handler发送消息后,消息会进行入队操作,在enqueueMessage方法中:
msg.targer = this;
this指的就是handler,所以handler被message持有了,而message放入消息队列后,message又被MessageQueue持有了,而MessageQueue是在创建Looper的时候生成的: