Handler

Handler

1. ThreadLocal

線程本地存儲(chǔ)為使用相同變量的每個(gè)不同線程都創(chuàng)建不同的存儲(chǔ),每個(gè)線程雖然使用同一個(gè)變量,但是變量狀態(tài)互不干擾。

  • 原理:
    每個(gè)線程有一個(gè)ThreadLocalMap 對(duì)象用來(lái)存儲(chǔ)該線程ThreadLocal類型的數(shù)據(jù)
class Thread implements Runnable {
  ...
      /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

ThreadLocalMap 是自定義HashMap,以ThreadLocal 對(duì)象為key,要使用的數(shù)據(jù)為value

當(dāng)調(diào)用ThreadLocal的get/set方法的時(shí)候首先獲得該線程的threadLocals變量,然后根據(jù)當(dāng)前的TreadLocal對(duì)象為key,在該線程中查找value。

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

對(duì)于一個(gè)線程來(lái)說,不同的ThreadLoacal對(duì)象都存在這個(gè)ThreadLocalMap 對(duì)象中,以ThreadLoacal對(duì)象為key查找對(duì)應(yīng)的值。

對(duì)于一個(gè)ThreadLoacal對(duì)象來(lái)說,數(shù)據(jù)的拷貝存儲(chǔ)在不同線程的ThreadLocalMap 對(duì)象中,每次操作(set/get)先找出對(duì)應(yīng)線程的ThreadLocalMap 對(duì)象。由此實(shí)現(xiàn)多個(gè)線程中互不干擾的存儲(chǔ)和修改數(shù)據(jù)

Handler實(shí)現(xiàn)原理

1. 消息Message和消息隊(duì)列MessageQueue

(1)Message

Message使用享元設(shè)計(jì)模式。定義:對(duì)象共享,避免創(chuàng)建多個(gè)對(duì)象

Message 不能直接new創(chuàng)建,需要調(diào)用其obtain方法

    Message message = Message.obtain();
    message.what = 100;
    mHandler.sendMessage(message);

Message.obtain 每次都是從Message對(duì)象池中選一個(gè)對(duì)象,而不是創(chuàng)建新的對(duì)象,當(dāng)一個(gè)消息處理完了之后,在Looper.loop 中調(diào)用完msg.target.dispachMessage(msg)之后會(huì)調(diào)用recycleUnchecked,將Message對(duì)象放到對(duì)象池中。
Message對(duì)象池中的對(duì)象以鏈表形式存儲(chǔ),每次obtain取出一個(gè)對(duì)象,sPoolSize--,每次recycleUnchecked 插入一個(gè)對(duì)象sPoolSize++,sPoolSize最大為50.

Message.java

   /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static 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;
            }
        }
        return new Message();
    }
 /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

(1)MessageQueue

主要方法:

  • enqueueMessage 往消息隊(duì)列中插入一條消息,按時(shí)間排序,消息隊(duì)列鏈表形式
    boolean enqueueMessage(Message msg, long when) {
        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) {
            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;
            //新消息時(shí)間最早,插入表頭,需要喚醒
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                //p.target == null 是“同步分割欄”,如果是同步分割欄,也需要調(diào)整喚醒時(shí)間
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            //喚醒隊(duì)列
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

needWake 表示最早的喚醒時(shí)間變了,需要喚醒隊(duì)列。

同步分割欄: 所謂“同步分割欄”,可以被理解為一個(gè)特殊Message,它的target域?yàn)閚ull。它不能通過sendMessageAtTime()等函數(shù)打入到消息隊(duì)列里,而只能通過調(diào)用Looper的postSyncBarrier()來(lái)打入。

“同步分割欄”就像一個(gè)卡子,卡在消息鏈表中的某個(gè)位置,當(dāng)消息循環(huán)不斷從消息鏈表中摘取消息并進(jìn)行處理時(shí),一旦遇到這種“同步分割欄”,那么即使在分割欄之后還有若干已經(jīng)到時(shí)的普通Message,也不會(huì)摘取這些消息了。請(qǐng)注意,此時(shí)只是不會(huì)摘取“普通Message”了,如果隊(duì)列中還設(shè)置有“異步Message”,那么還是會(huì)摘取已到時(shí)的“異步Message”的,然后在執(zhí)行普通Message。

frameworks/base/core/java/android/os/Message.java

public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}

源碼中 Choreographer中多次用到,為了保證繪制消息不被阻塞優(yōu)先執(zhí)行,防止繪制卡頓。

frameworks/base/core/java/android/view/Choreographer.java

 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
 msg.setAsynchronous(true);
 mHandler.sendMessageAtFrontOfQueue(msg);
  • next 循環(huán)計(jì)算下一個(gè)消息的時(shí)間,當(dāng)消息隊(duì)列中沒有消息或者下一個(gè)消息需要等待一段時(shí)間時(shí),不會(huì)一直跑for循環(huán),而是進(jìn)入睡眠狀態(tài),當(dāng)有消息到來(lái)時(shí),消息隊(duì)列被喚醒,查找新的消息并返回。
Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
                            }
            //沒有消息或者消息時(shí)間沒到。則進(jìn)入睡眠狀態(tài),使用epoll_wait
            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;
                //如果是同步分割欄則直接找到下個(gè)異步消息
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //下個(gè)消息的時(shí)間還沒到,則計(jì)算睡眠時(shí)間
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 沒有消息 -1 是一直等待
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }


            ... ...

        }
    }

2. Looper

Looper 采用線程本地存儲(chǔ)方式ThreadLocal,一個(gè)線程只有一個(gè)Looper

構(gòu)造函數(shù)可以看出,Looper里面定義本線程的消息隊(duì)列

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper使用demo

    class LooperThread extends Thread {
        public Handler mHandler;
  
        public void run() {
            Looper.prepare();
  
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
  
            Looper.loop();
        }
    }
  • Looper.prepare() 新建Looper對(duì)象,注意使用的是ThreadLocal
    /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
  • Looper.loop() 調(diào)用MessageQueue.next 得到消息,然后發(fā)給發(fā)消息的handler處理,最后回收消息recycleUnchecked
 public static void loop() {
     for (;;) {
         ... ...
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ... ... 
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ... ...
            msg.recycleUnchecked();
            ... ...
     }
 }

3. Handler處理消息

  1. msg.callback:先看是不是用Handler post方法發(fā)送的Runnable,如果是,則先執(zhí)行
  2. mCallback: 采用Handler handler = new Handler(callback)方式新建Handler ,傳進(jìn)來(lái)的callback 參數(shù)即是mCallback,如果是這種方式,則調(diào)用mCallback.handleMessage
  3. 最后調(diào)用該Handler的handleMessage
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容