Android 9.0 原始碼_機制篇 -- 全面解析 Handler

firefule發表於2021-09-09

開篇


上一篇說到曹操敗走華容道後...咳咳,不好意思走錯片場了。需要看上篇的同學出門左拐點主頁觀看

8.quit()

 public void quit() {
        mQueue.quit(false);          // 訊息移除
    }    public void quitSafely() {
        mQueue.quit(true);           // 安全訊息移除
    }

Looper.quit() 方法的實現最終呼叫的是 MessageQueue.quit() 方法。

9.MessageQueue.quit()

void quit(boolean safe) {        if (!mQuitAllowed) {    當 mQuitAllowed 為 false,表示不執行退出,強行呼叫 quit() 會丟擲異常
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {            if (mQuitting) {                return;
            }
            mQuitting = true;            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

訊息退出的方式:######1.當 safe = true 時,只移除尚未觸發的所有訊息,對於正在觸發的訊息並不移除######2.當 safe = flase 時,移除所有的訊息#三Handler-------------------------------------------------------------##建構函式-------------------------------------------------------------### 無參構造
public Handler() {    this(null, false);
}public Handler(Callback callback) {    this(callback, false);
}public Handler(boolean async) {    this(null, async);
}public Handler(Callback callback, boolean async) {    // 匿名類、內部類或本地類都必須申明為static,否則會警告可能出現記憶體洩露
    if (FIND_POTENTIAL_LEAKS) {     // 預設為 false
        final Class<? extends Handler> klass = getClass();        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
對於 Handler 的無參構造方法,預設採用當前執行緒 TLS 中的 Looper 物件,並且 callback 回撥方法為 null,且訊息為同步處理方式。只要執行的 Looper.prepare() 方法,那麼便可以獲取有效的 Looper 物件。

1.dispatchMessage()


在 Looper.loop() 中,當發現有訊息時,呼叫訊息的目標 handler,執行 dispatchMessage() 方法來分發訊息。

   /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            // 當 Message 存在回撥方法,回撥 msg.callback.run() 方法
            handleCallback(msg);
        } else {            if (mCallback != null) {                // 當 Handler 存在 Callback 成員變數時,回撥方法 handleMessage()
                if (mCallback.handleMessage(msg)) {                    return;
                }
            }            // Handler 自身的回撥方法 handleMessage()
            handleMessage(msg);
        }
    }

我們需要重點分析下這個函式:

首先會判斷 msg.callback 存不存在,msg.callback 是 Runnable 型別,如果 msg.callback 存在,那麼說明該 Message 是透過執行 Handler 的 post() 系列方法將 Message 放入到訊息佇列中的,這種情況下會執行 handleCallback(msg)。

2.handleCallback

原始碼如下:

    private static void handleCallback(Message message) {
        message.callback.run();
    }

這樣我們就清楚地看到我們執行了 msg.callback 的 run 方法,也就是執行了 post() 所傳遞的 Runnable 物件的 run 方法。

3.mCallback

如果我們不是透過 post() 系列方法將 Message 放入到訊息佇列中的,那麼 msg.callback 就是 null ,程式碼繼續往下執行。

接著我們會判斷 Handler 的成員欄位 mCallback 存不存在。mCallback 是 Hanlder.Callback 型別的,我們在上面提到過,在 Handler 的建構函式中我們可以傳遞 Hanlder.Callback 型別的物件,該物件需要實現 handleMessage 方法,如果我們在建構函式中傳遞了該 Callback 物件,那麼我們就會讓 Callback 的 handleMessage 方法來處理 Message。

    final Callback mCallback;    
    public interface Callback {        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }
如果我們在建構函式中沒有傳入 Callback 型別的物件,那麼 mCallback 就為 null ,那麼我們會呼叫 Handler 自身的 hanldeMessage 方法,該方法預設是個空方法,我們需要自己重寫實現該方法。
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {      // 空函式
    }

綜上所述,我們可以看到 Handler 提供了三種途徑處理 Message ,而且處理有前後優先順序之分:首先嚐試讓 post() 中傳遞的 Runnable 執行,其次嘗試讓 Handler 建構函式中傳入的 Callback 的 handleMessage 方法處理,最後才是讓 Handler 自身的 handleMessage 方法處理Message。

4.Callback

Callback 是 Handle r中的內部介面,需要實現其內部的 handleMessage 方法,Callback 程式碼如下:

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

Handler.Callback 是用來處理 Message 的一種手段,如果沒有傳遞該引數,那麼就應該重寫 Handler 的 handleMessage 方法,也就是說為了使得 Handler 能夠處理 Message ,有兩種辦法:

1.向 Hanlder 的建構函式傳入一個 Handler.Callback 物件,並實現 Handler.Callback 的 handleMessage 方法
2.無需向 Hanlder 的建構函式傳入 Handler.Callback 物件,但是需要重寫 Handler 本身的 handleMessage 方法

也就是說無論哪種方式,我們都得透過某種方式實現 handleMessage 方法,這點與 Java 中對 Thread 的設計有異曲同工之處。
在Java中,如果我們想使用多執行緒,有兩種辦法:

1. 向 Thread 的建構函式傳入一個 Runnable 物件,並實現 Runnable 的 run 方法
2. 無需向 Thread 的建構函式傳入 Runnable 物件,但是要重寫 Thread 本身的 run 方法

所以只要用過多執行緒 Thread,應該就對 Hanlder 這種需要實現 handleMessage 的兩種方式瞭然於心了。
在之前分析 Handler(用法篇)的時候我們講到過兩種重要的方法:sendMessage() 和 post(),我們從原始碼角度進行進一步分析!

四 sendMessage


我們看下 sendMessage() 原始碼處理流程:

public final boolean sendMessage(Message msg) {    return sendMessageDelayed(msg, 0);
}

1.sendMessageDelayed

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {        if (delayMillis < 0) {
            delayMillis = 0;
        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);     // 最終調 sendMessageAtTime()
    }

透過以上程式碼可以看書:sendMessage() 呼叫了 sendMessageDelayed() ,sendMessageDelayed() 又呼叫了 sendMessageAtTime()。

Handler 中還有 sendEmptyMessage() 方法:

    public final boolean sendEmptyMessage(int what)
    {        return sendEmptyMessageDelayed(what, 0);
    }    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;        return sendMessageDelayed(msg, delayMillis);
    }    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {        if (delayMillis < 0) {
            delayMillis = 0;
        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);     // 最終還是要調 sendMessageAtTime()
    }

由此可見所有的 sendMessage 方法和 sendEmptyMessage 最終都呼叫了 sendMessageAtTime 方法。

2.post

我們看下 post() 原始碼處理流程:

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

可以看到內部呼叫了 getPostMessage 方法,該方法傳入一個 Runnable 物件,得到一個 Message 物件。

3.getPostMessage

getPostMessage() 的原始碼如下:

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

透過上面的程式碼我們可以看到在 getPostMessage 方法中,我們建立了一個 Message 物件,並將傳入的 Runnable 物件賦值給 Message 的 callback 成員欄位,然後返回該 Message ,然後在 post 方法中該攜帶有 Runnable 資訊的 Message 傳入到 sendMessageDelayed 方法中。由此我們可以看到所有的 post 方法內部都需要藉助 sendMessage 方法來實現,所以 post() 與 sendMessage() 並不是對立關係,而是 post() 依賴 sendMessage() ,所以 post() 方法可以透過 sendMessage() 方法向訊息佇列中傳入訊息,只不過透過 post() 方法向訊息佇列中傳入的訊息都攜帶有 Runnable 物件(Message.callback)。

五 sendMessageAtTime


透過分別分析 sendEmptyMessage()、post() 方法與 sendMessage() 方法之間的關係,我們可以看到在 Handler 中所有可以直接或間接向訊息佇列傳送 Message 的方法最終都呼叫了 sendMessageAtTime 方法,該方法的原始碼如下:

   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);
   }

1.enqueueMessage

我們發現 sendMessageAtTime() 方法內部呼叫了 enqueueMessage() 函式:

   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
       msg.target = this;       if (mAsynchronous) {
           msg.setAsynchronous(true);
       }       return queue.enqueueMessage(msg, uptimeMillis);
   }
我們需要重點注意兩行程式碼:
msg.target = this; // 將 Message 的 target 繫結為當前的 Handler
// 變數 queue 表示的是 Handler 所繫結的訊息佇列 MessageQueue ,透過呼叫 queue.enqueueMessage(msg, uptimeMillis) 將 Message 放入到訊息佇列中。queue.enqueueMessage(msg, uptimeMillis);

還記得我們之前在分析 Looper 的時候,最終提到的 dispatchMessage() 嗎?我們回憶一下:

           try {
               msg.target.dispatchMessage(msg);
               end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
           } finally {               if (traceTag != 0) {
                   Trace.traceEnd(traceTag);
               }
           }

六 MessageQueue


每個執行緒內部都維護了一個訊息佇列 —— MessageQueue。訊息佇列 MessageQueue,顧名思義,就是存放訊息的佇列。那佇列中儲存的訊息是什麼呢?

假設我們在UI介面上單擊了某個按鈕,而此時程式又恰好收到了某個廣播事件,那我們如何處理這兩件事呢?因為一個執行緒在某一時刻只能處理一件事情,不能同時處理多件事情,所以我們不能同時處理按鈕的單擊事件和廣播事件,我們只能挨個對其進行處理,只要挨個處理就要有處理的先後順序。

為此Android把UI介面上單擊按鈕的事件封裝成了一個 Message ,將其放入到 MessageQueue 裡面去,即將單擊按鈕事件的 Message 入棧到訊息佇列中,然後再將廣播事件的封裝成以 Message ,也將其入棧到訊息佇列中。

也就是說一個 Message 物件表示的是執行緒需要處理的一件事情,訊息佇列就是一堆需要處理的 Message 的池。執行緒 Thread 會依次取出訊息佇列中的訊息,依次對其進行處理。

MessageQueue 中有兩個比較重要的方法,一個是 enqueueMessage 方法,一個是 next 方法。enqueueMessage 方法用於將一個 Messag e放入到訊息佇列 MessageQueue 中,next 方法是從訊息佇列 MessageQueue 中阻塞式地取出一個 Message。在 Android 中,訊息佇列負責管理著頂級程式物件(Activity、BroadcastReceiver等)以及由其建立的所有視窗。

七 建立MessageQueue


   MessageQueue(boolean quitAllowed) {
       mQuitAllowed = quitAllowed;       // 透過 native 方法初始化訊息佇列,其中 mPtr 是供 native 程式碼使用
       mPtr = nativeInit();
   }

next()

   Message next() {       final long ptr = mPtr;       if (ptr == 0) {     // 當訊息迴圈已經退出,則直接返回
           return null;
       }       // 迴圈迭代的首次為 -1
       int pendingIdleHandlerCount = -1; // -1 only during first iteration
       int nextPollTimeoutMillis = 0;       for (;;) {           if (nextPollTimeoutMillis != 0) {
               Binder.flushPendingCommands();
           }           // 阻塞操作,當等待 nextPollTimeoutMillis 時長,或者訊息佇列被喚醒,都會返回
           nativePollOnce(ptr, nextPollTimeoutMillis);           synchronized (this) {               // Try to retrieve the next message.  Return if found.
               final long now = SystemClock.uptimeMillis();
               Message prevMsg = null;
               Message msg = mMessages;               if (msg != null && msg.target == null) {                   // 當訊息 Handler 為空時,查詢 MessageQueue 中的下一條非同步訊息 msg,則退出迴圈
                   do {
                       prevMsg = msg;
                       msg = msg.next;
                   } while (msg != null && !msg.isAsynchronous());
               }               if (msg != null) {                   if (now < msg.when) {                       // 當非同步訊息觸發時間大於當前時間,則設定下一次輪詢的超時時長
                       nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                   } else {                       // 獲取一條訊息,並返回
                       mBlocked = false;                       if (prevMsg != null) {
                           prevMsg.next = msg.next;
                       } else {
                           mMessages = msg.next;
                       }
                       msg.next = null;                       // 設定訊息的使用狀態,即 flags |= FLAG_IN_USE
                       msg.markInUse();                       // 成功地獲取 MessageQueue 中的下一條即將要執行的訊息
                       return msg;
                   }
               } else {                   // 沒有訊息
                   nextPollTimeoutMillis = -1;
               }               // 訊息正在退出,返回null
               if (mQuitting) {
                   dispose();                   return null;
               }               // 當訊息佇列為空,或者是訊息佇列的第一個訊息時
               if (pendingIdleHandlerCount < 0
                       && (mMessages == null || now < mMessages.when)) {
                   pendingIdleHandlerCount = mIdleHandlers.size();
               }               if (pendingIdleHandlerCount <= 0) {               // 沒有 idle handlers 需要執行,則迴圈並等待
                   mBlocked = true;                   continue;
               }               if (mPendingIdleHandlers == null) {
                   mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
               }
               mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
           }           // 只有第一次迴圈時,會執行 idle handlers,執行完成後,重置 pendingIdleHandlerCount 為 0
           for (int i = 0; i < pendingIdleHandlerCount; i++) {               final IdleHandler idler = mPendingIdleHandlers[i];
               mPendingIdleHandlers[i] = null; // 去掉 handler 的引用

               boolean keep = false;               try {
                   keep = idler.queueIdle();   // idle 時執行的方法
               } catch (Throwable t) {
                   Log.wtf(TAG, "IdleHandler threw exception", t);
               }               if (!keep) {                   synchronized (this) {
                       mIdleHandlers.remove(idler);
                   }
               }
           }           // 重置 idle handler 個數為 0,以保證不會再次重複執行
           pendingIdleHandlerCount = 0;           // 當呼叫一個空閒 handler 時,一個新 message 能夠被分發,因此無需等待可以直接查詢 pending message
           nextPollTimeoutMillis = 0;
       }
   }

nativePollOnce 是阻塞操作,其中 nextPollTimeoutMillis 代表下一個訊息到來前,還需要等待的時長;當 nextPollTimeoutMillis = -1 時,表示訊息佇列中無訊息,會一直等待下去。

當處於空閒時,往往會執行 IdleHandler 中的方法。當 nativePollOnce() 返回後,next() 從 mMessages 中提取一個訊息。

八 enqueueMessage()

    boolean enqueueMessage(Message msg, long when) {        // 每一個普通 Message 必須有一個 target
        if (msg.target == null) {            throw new IllegalArgumentException("Message must have a target.");
        }        if (msg.isInUse()) {            throw new IllegalStateException(msg + " This message is already in use.");
        }        synchronized (this) {            // 正在退出時,回收 msg,加入到訊息池
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                // p 為 null (代表MessageQueue沒有訊息) 或者 msg 的觸發時間是佇列中最早的,則進入該該分支
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;   // 當阻塞時需要喚醒
            } else {                // 將訊息按時間順序插入到 MessageQueue。一般地,不需要喚醒事件佇列,除非
                // 訊息隊頭存在 barrier,並且同時 Message 是佇列中最早的非同步訊息
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                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;
            }            // 訊息沒有退出,我們認為此時 mPtr != 0
            if (needWake) {
                nativeWake(mPtr);
            }
        }        return true;
    }

MessageQueue 是按照 Message 觸發時間的先後順序排列的,隊頭的訊息是將要最早觸發的訊息。當有訊息需要加入訊息佇列時,會從佇列頭開始遍歷,直到找到訊息應該插入的合適位置,以保證所有訊息的時間順序。

九 removeMessages()


   void removeMessages(Handler h, int what, Object object) {       if (h == null) {           return;
       }       synchronized (this) {
           Message p = mMessages;           // 從訊息佇列的頭部開始,移除所有符合條件的訊息
           while (p != null && p.target == h && p.what == what
                  && (object == null || p.obj == object)) {
               Message n = p.next;
               mMessages = n;
               p.recycleUnchecked();
               p = n;
           }           // 移除剩餘的符合要求的訊息
           while (p != null) {
               Message n = p.next;               if (n != null) {                   if (n.target == h && n.what == what
                       && (object == null || n.obj == object)) {
                       Message nn = n.next;
                       n.recycleUnchecked();
                       p.next = nn;                       continue;
                   }
               }
               p = n;
           }
       }
   }

這個移除訊息的方法,採用了兩個 while 迴圈,第一個迴圈是從隊頭開始,移除符合條件的訊息,第二個迴圈是從頭部移除完連續的滿足條件的訊息之後,再從佇列後面繼續查詢是否有滿足條件的訊息需要被移除。

總結


最後用一張圖,來表示整個訊息機制:


圖片描述

63b637532d65d2a632369cbe8315b5bb_1460000016798930.jpg

圖解:

     Handler透過sendMessage()傳送Message到MessageQueue佇列;
     Looper透過loop(),不斷提取出達到觸發條件的Message,並將Message交給target來處理;
     經過dispatchMessage()後,交回給Handler的handleMessage()來進行相應地處理。
     將Message加入MessageQueue時,處往管道寫入字元,可以會喚醒loop執行緒;如果MessageQueue中沒有Message,並處於Idle狀態,則會執行IdelHandler介面中的方法,往往用於做一些清理性地工作。

想學習更多Android知識,或者獲取相關資料請加入Android技術開發交流2群:935654177。本群可免費獲取Gradle,RxJava,小程式,Hybrid,移動架構,NDK,React Native,效能最佳化等技術教程!

圖片描述

面向Android中的一切實體.png



作者:Android征途
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1834/viewspace-2821457/,如需轉載,請註明出處,否則將追究法律責任。

相關文章