常見有內(nèi)存泄漏的寫法
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
解決方案
private final MyHandler handler = new MyHandler(this);
private static class MyHandler extends Handler {
private final WeakReference<Activity> weakAcitivity;
public MyHandler(Activity activity) {
weakAcitivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = weakAcitivity.get();
if (null != activity) {
// coding
}
}
}
為什么會出現(xiàn)內(nèi)存泄漏
注:以下均以在主線程new Handler()舉例,在其他線程初始化Handler情況類似
跨進(jìn)程原理概述
Handler跨線程原理是主線程中持有一個MessageQueue,在新線程中對這個隊列進(jìn)行插入(sendMessage()),然后在主線程循環(huán)讀取然后通過調(diào)用callback,再調(diào)用handleMessage()實現(xiàn)跨線程的。
內(nèi)存泄漏原理概述
因為很有可能會有好幾個Handler同時向主線程MessageQueue 插入數(shù)據(jù),而callback的時候需要回調(diào)到各自的Handler中去,所以插入MessageQueue中的實例Message會持有當(dāng)前handler實例。而MessageQueue的生命周期是和當(dāng)前主線程的生命周期一致的,如果Acitivity持有handler的話,那就必然會造成內(nèi)存泄漏了。
源碼分析
-
所有的
sendMessage()最終都會調(diào)用下面代碼public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } -
而在
enqueueMessage()中,它會把this,也就是當(dāng)前handler保存在msg中,然后插入到當(dāng)前主線程的MessageQueue中。到此為止,真相大白。private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } -
回調(diào)
callback,在Looper.java中不斷循環(huán)MessageQueue從中取出Message(只顯示了部分關(guān)鍵代碼)public static void loop() { for (;;) { try { msg.target.dispatchMessage(msg); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } } } -
回到
Handler中,看看被Looper調(diào)用的dispatchMessage()方法:public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } 至此,真相得到了驗證。