Handler---4部曲---3. MessageQueue隊列

Handler---4部曲---1. 總體流程
Handler---4部曲---2. ThreadLocal 存儲主流程
Handler---4部曲---3. MessageQueue隊列
Handler---4部曲---4.細節補充

本章內容

一. 數據結構

MessageQueue 3個成員變量

  ArrayList<IdleHandler> mIdleHandlers   --->
  boolean mBlocked;  --->next()是否阻塞 標記
  Message mMessages;  --->隊列頭

Message 3個成員變量

   Handler target; //當前handler
   long when;  //消息延遲時間
   Message next;//下一個消息

二. 消息分類

2.1 同步消息
2.2 異步消息
2.3 同步屏障消息 --->撤銷屏障removeSyncBarrier()
2.4 空閒消息

三. 4種消息的添加方式

3.1 同步消息
enqueueMessage(Message msg, long when) 添加

boolean enqueueMessage(Message msg, long when) {
        //用此方法添加到隊列, msg一定要給target賦值
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            msg.when = when;
            Message p = mMessages;
            //如果 首次進來 || 時間=0 || 當前時間<隊列頭的時間
           //就把當前消息加到隊列頭
            if (p == null || when == 0 || when < p.when) { //
                msg.next = p;
                mMessages = msg;
            } else {
                //把當前msg按照when 從小到大插入隊列中
                Message prev;
                for (;;) {
                    prev = p; //
                    p = p.next;
                    if (p == null || when < p.when) { 
                        break;
                    }
                }
                //此時  prev.when<msg.when<p.when
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
        }
        return true;
    }

總結:把msg消息按時間順序, 從小到大添加到隊列中.

3.2 異步消息
和同步消息差不多, 只是把msg標記爲異步.

 msg.setAsynchronous(true)  
 MessageQueue.enqueueMessage(msg, 時間xxx)

3.3 同步屏障消息

//添加同步屏障消息 
int token = MessageQueue.postSyncBarrier();
//移除同步屏障消息
removeSyncBarrier(token);
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        synchronized (this) {
            //獲取Message 對象
            final Message msg = Message.obtain(); 
            msg.when = when; //時間賦值---當前時間
            // 以下的操作,和普通消息一樣,按時間順序 小到大排列
           // 最後msg就是隊列頭.
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

總結:

msg內部創建,也是按時間順序進行排列的,
msg.target = null
msg.when = 當前時間

3.4 空閒消息
將消息添加到mIdleHandlers集合中

    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

四. next()消息處理

    Message next() {
        //mIdleHandlers集合數量
        int pendingIdleHandlerCount = -1;
        //阻塞休眠時間
        int nextPollTimeoutMillis = 0;
        for (;;) {
            //阻塞隊列   nextPollTimeoutMillis休眠時間
            // -1 一直休眠, 等待喚醒
            // 0  不休眠, 馬上執行
            // 大於0,休眠時間
            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());
                }
               //注意:如果異步消息 的時間  > 當前時間, 則會一直等待, 其它消息得不到執行, 直到異步消息執行完爲止.
                
                //此時msg代表取出的消息
                if (msg != null) { //有消息
                    if (now < msg.when) { //沒有到執行時間
                        //休眠時間 = (時間差,Integer.MAX_VALUE)中的小值.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 馬上執行消息
                        mBlocked = false; //標記線程不阻塞
                        if (prevMsg != null) { //異步消息時
                            prevMsg.next = msg.next;
                        } else { //同步消息時,隊列頭=下一個msg
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        return msg;
                    }
                } else {
                    // 如果沒有消息,休眠時間--->一直休眠
                    nextPollTimeoutMillis = -1;
                }

                //是否退出(下一篇會講)
                if (mQuitting) {
                    dispose();
                    return null;
                }


//>>>>>>>>>>>>>>>下面是處理 空閒消息 mIdleHandlers>>>>>>>>>>>>>>>>>>>>>>>>>>
                if (pendingIdleHandlerCount < 0  //首次進入
                        //&& (沒有消息 || 當前時間 < 隊列頭時間)
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //沒有空閒消息處理
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;  //標記,線程阻塞
                    continue;
                }
               //空閒消息List 轉 數組
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
           }
            
            //循環處理空閒消息
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // 釋放數組內存

                boolean keep = false;
                try {
                    // //返回false纔會刪除任務, 否則重複執行
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            //重置變量
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }
Message消息處理:
  1. 首先獲取隊列頭:Message msg = mMessages;
  2. 判斷是否有同步屏障的消息. 如果有,會循環隊列, 去找異步消息msg
    ①. 如果有異步消息,msg = 異步消息
    ②. 如果沒有異步消息,msg==null.
  3. 如果msg==null, 線程進入休眠,nextPollTimeoutMillis = -1;
  4. 如果 msg!=null;
    ①. 當前時間 < msg.when, nextPollTimeoutMillis(休眠時間) = msg.when-當前時間
    ②. 當前時間 >= msg.when,取出msg給loop去處理
    ------>同步消息時,隊列頭=下一個msg, mMessages = msg.next
空閒消息處理:

沒有msg消息處理的時候,纔會處理空閒消息

  1. mIdleHandlers集合 轉 數組
  2. 遍歷數組 處理任務
  3. 任務返回false時, 纔會從mIdleHandlers集合移除任務.
  4. 重置變量
endingIdleHandlerCount = 0; 
nextPollTimeoutMillis = 0;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章