Handler 消息機制以及內存泄漏

1. 消息機制

1.1 post系列

經過查看源碼可知,post(Runnable r)postDelayed(Runnable r, long delayMillis)最終調用的都是sendMessageDelayed方法:java

// post
 public final boolean post(Runnable r){
    return sendMessageDelayed(getPostMessage(r), 0);
}
// postDelayed
public final boolean postDelayed(Runnable r, long delayMillis){
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

1.2 postAtTime

postAtTime(Runnable r, long uptimeMillis)最終調用的是sendMessageAtTime方法:segmentfault

// postAtTime
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

這裏面都有一個共同的方法getPostMessageide

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

m.callback = r這句能夠看出:getPostMessage就是把傳入的 Runnable 賦值給 Message 對象的 callback 屬性函數

1.3 sendEmptyMessage

sendEmptyMessage最終指向sendEmptyMessageDelayed函數:oop

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

msg.what = what這句能夠看出:sendEmptyMessageDelayed就是把 what 賦值給 Message 的 what 屬性。post

1.4 sendMessage(msg : Message)

至於經常使用的sendMessage(msg : Message)就不用細說了,這是直接傳入 Message 類型的參數。this

綜合以上這幾點來講,各類發送消息的方法最終都是把消息賦值給 Message 對象(或者 Message 的屬性),並且這些方法最終調用的都是 MessageQueue 中的 enqueueMessage方法,就是把消息加入消息隊列

1.5 enqueueMessage方法

方法較長,咱們看看關鍵的幾行:spa

Message prev;
for (;;) {
    prev = p;
    p = p.next;
    if (p == null || when < p.when) {
        break;
    }
    if (needWake && p.isAsynchronous()) {
        needWake = false;
    }
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;

用一個無限循環將消息加入到消息隊列中(鏈表的形式),到這裏把消息發出去並加入隊列這兩步算是完成了,接下來就是取出並處理消息。.net

1.6 Looper 取出消息

Looper 中有一個死循環(Looper.loop())用來不斷從隊列中取出消息:線程

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next();
        ...代碼省略
        msg.target.dispatchMessage(msg);
        ...代碼省略
        msg.recycleUnchecked();
    }
}

queue.next()每次取出一條 Message 消息,而後交由msg.targer.dispatchMessage(msg)處理,從上篇文章中能夠知道,msg.targer就是發出消息的 Handler,因此咱們只須要關注dispatchMessage(msg)

1.7 dispatchMessage(msg)處理消息

dispatchMessage(msg)在 Handler 類中

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
  1. msg 的 callback 不爲空,調用handleCallback方法(message.callback.run())
  2. mCallback 不爲空,調用mCallback.handleMessage(msg)
  3. 最後若是其餘都爲空,執行 Handler 自身的handleMessage(msg)方法
第 1 點就是上面的 【1.1 post系列】 和 【1.2 postAtTime】,第 3 點就是咱們最多見的 handleMessage方法。須要注意一下就是 callback.run()這裏直接調用線程的 run 方法,至關於普通方法調用, 不會開啓新的線程

如今談談第 2 點,Handler 有不少種構造方法,除了上一篇文章提到的public Handler(Looper looper)Handler()等,還有一個:

public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

Callback 是這樣的:

public interface Callback {
        public boolean handleMessage(Message msg);
    }

須要重寫handleMessage,這不就是 Handler 裏面的handleMessage嗎?其實二者是有區別的:

// Handler
public void handleMessage(Message msg) {}


// Callback
public boolean handleMessage(Message msg);

Callback 裏面的handleMessage返回值是 Boolean 類型的,那麼接下來分別返回 true 和 false 看看效果吧:

class MainActivity : AppCompatActivity() {
    var handler: Handler? = null
    var looper: Looper? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        looper = Looper.getMainLooper()
        val callback = object: Handler.Callback{
            override fun handleMessage(msg: Message?): Boolean {
                Log.e("abc","--- Callback:threadName ---" + Thread.currentThread().name
                )
                return true
            }
        }
        val thread = object : Thread() {
            override fun run() {
                super.run()
                handler = object : Handler(looper, callback) {
                    override fun handleMessage(msg: Message?) {
                        super.handleMessage(msg)
                        Log.e("abc","--- handleMessage:threadName ---" + Thread.currentThread().name
                        )
                    }
                }
            }
        }

        thread.start()

        myBtn.setOnClickListener {
            handler?.sendEmptyMessage(4)
        }
    }
}

// Log 打印狀況
--- Callback:threadName ---main

若是返回值類型改爲 false:

val callback = object: Handler.Callback{
            override fun handleMessage(msg: Message?): Boolean {
                Log.e("abc", "--- Callback:threadName ---" + Thread.currentThread().name
                )
                return false
            }
        }
        
// Log 打印狀況
--- Callback:threadName ---main
--- handleMessage:threadName ---main

因此,Callback 中的handleMessage返回 true 就不繼續執行 Handler 中的handlerMessage了,反之則兩個handleMessage都執行。其實這些從dispatchMessage方法中能夠看出來(返回 true 則 return 終止,不然繼續執行 handleMessage):

if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
        return;
    }
}
handleMessage(msg);

總結

上面主要講了消息的發送和取出,大概知道了 Handler 消息機制的工做流程:

  1. Handler 對象經過post(postDelayedpostAtTime)或者sendMessagesendEmptyMessage)把消息(Message)交給 MessageQueue
  2. MessageQueue.enqueueMessage方法將 Message 以鏈表的形式放入消息隊列中
  3. Looper.loop()循環調用 MessageQueue 的next()取出消息,交給 Handler 的dispatchMessage方法處理消息
  4. dispatchMessage()中分別判斷msg.callback和構造函數傳入的mCallback是否爲空,不爲空則執行它們的回調,爲空則執行 Handler 自身的handlerMessage方法。

2. Handler 內存泄漏問題

2.1 引發內存泄漏的緣由

下面這樣寫會有內存泄漏風險:

val mHandler = object : Handler() {
        override fun handleMessage(msg: Message?) {
            super.handleMessage(msg)
        }
    }

Android Studio 會標黃警告,鼠標放在 handler 代碼塊部分還會彈出提示,大概意思就是建議你用靜態模式或者弱引用。上面這種寫法至關於定義了一個匿名內部類,非靜態的匿名內部類默認是持有外部類(對應 Activity 等)引用的。若是發消息的 handler 所在線程還在執行,當前 Activity 就被 finish 了,那麼該 Handler 的匿名內部類持有 Activity 的引用,因此 Activity 對象是沒法被 GC 機制回收的。即:執行了 finish 代碼,但 Activity 對象還在內存中(內存泄漏)。這種對象若是愈來愈多,就會有 OOM(內存溢出)的可能。

2.2 解決辦法

kotlin 中沒有靜態類這個概念,這裏用 java 靜態內部類舉例:

static class MyHandler extends Handler {
        WeakReference<MyActivity> weakActivity;

        MyHandler(MyActivity activity) {
            weakActivity = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MyActivity activity = weakActivity.get();
            // activity.text = "......"
        }
    }
  1. 靜態內部類不持有外部類引用,因此不會致使 Activity 對象泄漏(java 中 「靜態的」等於「類的」,靜態內部類若是能持有外部類引用,那說明外部類的引用就是內部「類的」,這不符合邏輯,這樣寫編譯都不經過)。
  2. 但該靜態內部類必須使用外部類的引用(好比操做 UI),此時就能夠用弱引用的方式。上面代碼用的是把 Activity 的弱引用在 Handler 構造函數中初始化,這樣若是須要操做 UI,可使用activity.text = "test"這種方式。

參考文章:

  1. 從Handler.post(Runnable r)再一次梳理Android的消息機制(以及handler的內存泄露
  2. Handler內存泄露及解決方案
相關文章
相關標籤/搜索