關於Handler.removemessages方法

powerx_yc發表於2017-02-16

在閱讀老人的原始碼中,發現以下程式碼片段

private handleMessage(Message msg) {
    switch(msg.what) {
        case ACTION_XXX:
            // do something...
            this.removeMessages(ACTION_XXX);
            break;

            //other cases
        }
} 

1. 猜想

初步猜測其作用是為了處理某訊息後,清除在訊息佇列中等待的同類訊息,以達到防止在處理過程中重複傳送的目的

2. 驗證

於是寫一個demo驗證之(因為上述原始碼觸發效果過為複雜,所以不用此進行驗證)

mHandler.setHandlerCallBack(new MyHandler.MyHandlerCallBack() {
    @Override
    public void handleMessage(Message message) {
        Log.i(TAG, "handleMessage: " + message.arg1);
        if (message.what == ACTION_ODD) {
            mMyHandler.removeMessages(ACTION_ODD);
        }
    }
});

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Message message = Message.obtain();
        message.what = number % 2 == 0 ? ACTION_EVEN : ACTION_ODD;
        message.arg1 = number;
        
        // 將handleMessage延遲1s呼叫
        mHandler.sendMessageDelayed(message, 1000);
        number++;
    }
});

快速點選Button後,觀察log日誌輸出

02-16 10:51:08.787 17454-17454/com.daijie.handlerapplication I/MainActivity: handleMessage: 0
02-16 10:51:08.958 17454-17454/com.daijie.handlerapplication I/MainActivity: handleMessage: 1
02-16 10:51:09.112 17454-17454/com.daijie.handlerapplication I/MainActivity: handleMessage: 2
02-16 10:51:09.489 17454-17454/com.daijie.handlerapplication I/MainActivity: handleMessage: 4
02-16 10:51:09.762 17454-17454/com.daijie.handlerapplication I/MainActivity: handleMessage: 6
02-16 10:51:10.100 17454-17454/com.daijie.handlerapplication I/MainActivity: handleMessage: 8
02-16 10:51:10.237 17454-17454/com.daijie.handlerapplication I/MainActivity: handleMessage: 9

3. 推論

從日誌輸出可以得知偶數全部被輸出,而1~9中的奇數被過濾了
可以推斷handleMessage在輸出Message.what==1的log結束後,呼叫Handler.removeMessages將Message.what為ACTION_ODD從訊息佇列中移除,所以中間的訊息被過濾掉了。

4. 檢視原始碼

檢視原始碼是最好的驗證方式,所以先檢視Handler的removeMessages方法原始碼

// Handler.java
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

可以看出,這裡實際上是移除訊息佇列的message,再看看MessageQueue中的removeMessages方法原始碼

// MessageQueue.java
void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
         Message p = mMessages;

        // 原始碼註釋,Remove all messages at front.
        // 根據上面傳入的引數,p!=null成立,p.target == h成立,object == null 成立
        // 即此處可等同為p.what == what
        // 此處程式碼即找到第一個p.what == what的訊息並將其移除
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        // 此處即為移除找到第一個後的Message
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                // 同上,此處也可以等同為p.what == what
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

由原始碼中可以看出,MessageQueue將Message.what與函式傳入的what相同的Message從佇列中移除,和推論相符


這是本人第一次進行探討,如有什麼錯誤,請在評論區進行糾正。

相關文章