Handler究竟是如何泄漏內(nèi)存的

常見有內(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)存泄漏了。

源碼分析
  1. 所有的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);
    }
    
  2. 而在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);
    }
    
  3. 回調(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);
                            }
                    }
            }
    }
    
  4. 回到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);
             }
    }
    
  5. 至此,真相得到了驗證。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容