为什么Handler可能导致内存泄露?
一、匿名内部类或非静态内部类默认持有外部类引用对象。
通常使用Handler会有如下几种方式:
// 1.匿名内部类
Handler handler = new Handler(Looper.getMainLooper()) {
// 监听处理Message
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
// 2.非静态内部类
MyHandler myHandler = new MyHandler();
class MyHandler extends Handler {
// 监听处理Message
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
}
// 3.直接执行匿名内部类或非静态内部类
Handler handler = new Handler();
handler.post(new Runnable() {
// 持有外部类对象
@Override
public void run() {}
});
这几种方式无论哪一种都将导致Handler对象持有外部类对象。
假如在Activity中定义了一个Handler用来执行更新UI操作事件,当Activity关闭/销毁时,Handler也会随之销毁,那么就不会存在内存泄露这种说法。但是当Activity关闭/销毁时,由于Handler持有Activity的引用对象,导致Activity需要进行GC回收时无法进行回收,这个时候就会出现内存泄露。
二、Handler相关引用链
Activity -> Handler -> Message -> MessageQueue -> ThreadLocal -> Thread。
刚刚讲到Handler持有Activity的引用对象导致Activity无法进行GC回收(可达性分析),从而引发内存泄露。那么为什么会导致Activity无法进行回收呢?
当然引发内存泄露还有一个重要条件,就是Handler中有还未执行完的Message。
Message类中有一个Handler target属性,用来指明消费Message消息是哪个Handler,而在以上场景中,这个target变量自然指向的是Activity中自定义的Handler。
我们知道Message消息执行是需要通过Looper不断地轮询MessageQueue,而MessageQueue是用来保存Message的容器,当然MessageQueue也是Looper的属性变量。这样未执行完的Message必然会被MessageQueue所持有,而MessageQueue又被Looper所持有。
而这里的Looper对象实际上是static Looper sMainLooper,该变量是在ActivityThread中被创建的静态变量,静态变量可作为GC Root 对象,所以Looper对象不可能被回收,这就间接导致MessageQueue、Message以及Handler不可能被回收,这便是导致Activity内存泄露的原因。
如果该Looper是开发者自定义的非静态对象,也会导致内存泄漏。
原因是Looper对象中存在ThreadLocal<Looper> sThreadLocal静态变量,该变量使用其静态内部类 ThreadLocalMap 类似键值对的方式保存着Looper对象和Thread对象,而Thread对象也会持有ThreadLocal对象,所以Thread如果不会被回收,那么Looper对象肯定也不会被回收。
public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
// 创建 Map 并放到了 Thread 中
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
}
三、如何避免Handler导致的内存泄露?
两种方式可以避免Handler导致的内存泄露:
- 采用静态内部类。
静态内部类不会持有外部类的引用对象。
MyHandler myHandler = new MyHandler(this);
private static class MyHandler extends Handler {
private WeakReference<Activity> mWeakRef;
public MyHandler(Activity activity) {
mWeakRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
}
- 在Activity销毁时移除所有Message。
onDestroy() {
handler.removeCallbacksAndMessages(null);
handler = null;
}

