快速理解Handler機制

前期準備 1 、2、 3 步驟

使用 4、5、6步驟

1. ActivityThread main方法(ActivityThread類中)

 Looper.prepareMainLooper();// 爲UI(祝線程)創建1個循環器對象
 Looper.prepare() // 爲當前線程(子線程)創建1個循環器對象
 Looper.loop();// 開啓輪循

2. prepareMainLooper() ( Looper類中)

  1. UI線程由prepareMainLooper函數內部調用自動生成Looper
  2. 子線程手動調用Looper.prepare()生成Looper對象
 public static final void prepare() {
    // sThreadLocal用於存儲線程的變量
        sThreadLocal.set(new Looper(true));
    }

2.1 Looper構造函數

  private Looper(boolean quitAllowed) {

            mQueue = new MessageQueue(quitAllowed);
            // 1. 創建1個消息隊列對象(MessageQueue)
            // 即 當創建1個Looper實例時,會自動創建一個與之配對的消息隊列對象(MessageQueue)
            mThread = Thread.currentThread();
        }

3. Looper.loop();( Looper類中)

開啓消息輪循

/** 
  * 源碼分析: Looper.loop()
  * 作用:消息循環,即從消息隊列中獲取消息、分發消息到Handler
  * 特別注意:
  *       a. 主線程的消息循環不允許退出,即無限循環
  *       b. 子線程的消息循環允許退出:調用消息隊列MessageQueue的quit()
  */
  public static void loop() {//只貼核心代碼
       //1. 獲取當前Looper的消息隊列
            final Looper me = myLooper();
            final MessageQueue queue = me.mQueue;
                for (;;) {
       //2. next()取出消息
            Message msg = queue.next(); // might block
       //3. 分發消息:調用msg內部的Handler的dispatchMessage
            msg.target.dispatchMessage(msg);
            // 釋放資源
            msg.recycleUnchecked();
        }
  }
        

3.1 next()取出消息( MessageQueue類中)

next()是MessageQueue中方法

Message next() {
     
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            // native層調用,沒有消息會在這裏阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);

           
        }
    }

3.2 dispatchMessage分發消息 ( Handler 類中)

dispatchMessage是Handler中的方法

  public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 使用者處理消息
            handleMessage(msg);
        }
    }

使用

4 .new Handler()

4.1 Handler構造方法做了什麼事情?

  1. 獲取當前線程Looper
  2. 關聯MessageQueue
 public Handler(Callback callback, boolean async) {
           // 僅貼出關鍵代碼
            // 1. 指定Looper對象
                mLooper = Looper.myLooper();
            // 2. 綁定消息隊列對象(MessageQueue)
                mQueue = mLooper.mQueue;
                // 獲取該Looper對象中保存的消息隊列對象(MessageQueue)
                // 至此,保證了handler對象 關聯上 Looper對象中MessageQueue
    }

5. 創建消息對象Message

Message msg = Message.obtain()

  • Message內部維護了1個Message池,用於Message消息對象的複用
  • 使用obtain()則是直接從池內獲取:避免每次都使用new重新分配內存
  • 若池內無消息對象可複用,則還是用關鍵字new創建

/** 
  * 源碼分析:Message.obtain()
  * 作用:創建消息對象
  * 注:創建Message對象可用關鍵字new 或 Message.obtain()
  */
  public static Message obtain() {

        // Message內部維護了1個Message池,用於Message消息對象的複用
        // 使用obtain()則是直接從池內獲取
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
            // 建議:使用obtain()”創建“消息對象,避免每次都使用new重新分配內存
        }
        // 若池內無消息對象可複用,則還是用關鍵字new創建
        return new Message();

    }

6. 在工作線程中 發送消息到消息隊列中

最後會都會調用enqueueMessage方法(Handler類中)


            private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
                 // 1. 將msg.target賦值爲this
                 // 即 :把 當前的Handler實例對象作爲msg的target屬性
                 msg.target = this;
                 // 請回憶起上面說的Looper的loop()中消息循環時,會從消息隊列中取出每個消息msg,然後執行msg.target.dispatchMessage(msg)去處理消息
                 // 實際上則是將該消息派發給對應的Handler實例        

                // 2. 調用消息隊列的enqueueMessage()
                // 即:Handler發送的消息,最終是保存到消息隊列->>分析4
                return queue.enqueueMessage(msg, uptimeMillis);
        }

6.1 enqueueMessage

屬於消息隊列類(MessageQueue類)的方法

        /** 
          * 定義:屬於消息隊列類(MessageQueue)的方法
          * 作用:入隊,即 將消息 根據時間 放入到消息隊列中(Message ->> MessageQueue)
          * 採用單鏈表實現:提高插入消息、刪除消息的效率
          */
          boolean enqueueMessage(Message msg, long when) {

                synchronized (this) {

                    msg.markInUse();
                    msg.when = when;
                    Message p = mMessages;
                    boolean needWake;

                    // 判斷消息隊列裏有無消息
                        // a. 若無,則將當前插入的消息 作爲隊頭 & 若此時消息隊列處於等待狀態,則喚醒
                        if (p == null || when == 0 || when < p.when) {
                            msg.next = p;
                            mMessages = msg;
                            needWake = mBlocked;
                        } else {
                            needWake = mBlocked && p.target == null && msg.isAsynchronous();
                            Message prev;

                        // b. 判斷消息隊列裏有消息,則根據 消息(Message)創建的時間 插入到隊列中
                            for (;;) {
                                prev = p;
                                p = p.next;
                                if (p == null || when < p.when) {
                                    break;
                                }
                                if (needWake && p.isAsynchronous()) {
                                    needWake = false;
                                }
                            }

                            msg.next = p; 
                            prev.next = msg;
                        }

                        if (needWake) {
                            nativeWake(mPtr);
                        }
                    }
                    return true;
            }

send…和 post…區別

  1. 消息對象的創建 = 內部 根據Runnable對象而封裝
  2. 發送到消息隊列的邏輯 =sendM(Message msg)

常問問題

  1. Looper 死循環爲什麼不會導致應用卡死?
    會阻塞在MessageQueue的next方法nativePollOnce()方法裏,此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,通過往pipe管道寫端寫入數據來喚醒主線程工作

Handler 同步屏障

  1. 如何設置同步屏障?

下面 postSyncBarrier 方法創建一個MSG對象,但是MSG的target屬性沒有賦值

MessageQueue類:

    private int postSyncBarrier(long when) {
       
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
        // .....
            return token;
        }
    }

Handler.postXXXX/sendXXXX,最後調用enqueueMessage時,都會爲MSG的target賦值

Handler類:


    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
            // 爲target賦值
        msg.target = this;
       //....
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看出設置同步屏障,創建的msg,內部的target是空值。Handler發送消息最終都會在enqueueMessage中進行target賦值

MessageQueue.next()

同步屏障會在取消息中體現出來。

如果設置了同步屏障,Handler會優先處理異步消息

 Message next() {
       //....
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            // 沒有消息,進入休眠狀態
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
              
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // 同步屏障,過濾掉同步消息,找到最近的異步消息
                    // 異步消息優先級高於同步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
              
               //....
        }
    }

應用場景:

Activity啓動後,在繪製前中有使用到同步屏障。

ViewRootImpl類:

 void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 使用同步屏障,確保mTraversalRunnable先執行
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
         //....
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章