Android 8.1 Handler 源碼解析

源碼解析,如需轉載,請註明做者:Yuloran (t.cn/EGU6c76)java

一. 前言

基於Android 8.1(API27) 源碼,分析 Handler 的工做流程。linux

在 Android 系統中,Zygote 進程是首個 java 進程,同時也是全部 java 進程的父進程。上層應用開發工程師所關注的 App 進程 fork 自 Zygote 進程,App 進程建立後最後會經過反射,調用 ActivityThread 的 main() 方法,進而初始化主線程的 looper 和 handler。git

二. 主線程 Looper 初始化

2.1 ActivityThread.main

ActivityThread.java數組

public static void main(String[] args) {
        // 省略...
		
        // 初始化 UI 線程的 looper 對象
        Looper.prepareMainLooper();
        
        // 初始化 ActivityThread,進而初始化其成員變量 mH(Handler子類)
        ActivityThread thread = new ActivityThread();
        // 將 ApplicationThread(Binder) 對象 attach 到 ActivityManagerService(AMS)
        // 注:AMS 運行在 SystemServer 進程的一個線程中,負責調度四大組件等,經過 Binder 與 App 進程進行 IPC
        thread.attach(false);
        
        // 省略...
		
        // 主線程進入循環
        Looper.loop();
    }
複製代碼

2.2 Looper.prepareMainLooper

Looper.java數據結構

public static void prepareMainLooper() {
        // 初始化主線程 looper,不容許退出
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // 初始化 sMainLooper,便於經過 new Handler(Looper.getMainLooper()) 方式向主線程發消息
            sMainLooper = myLooper();
        }
    }
複製代碼

2.3 Looper.prepare

Looper.javaapp

// sThreadLocal 爲 ThreadLocal<Looper> 類型的靜態變量
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    private static void prepare(boolean quitAllowed) {
        // 一個線程只能有一個 looper 對象,不然拋出異常
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 此處實際是將 looper 對象存儲到了 Thread.java 的成員變量 threadLocals(ThreadLocalMap) 中
        sThreadLocal.set(new Looper(quitAllowed));
    }
複製代碼

sThreadLocal 對象是 Looper.java 中的靜態變量,故只要 Looper.class 不被 jvm 卸載,該變量就不會從新初始化。jvm

2.4 Looper

Looper.javaasync

private Looper(boolean quitAllowed) {
        // 初始化 MessageQueue,由於一個線程只有一個 looper,因此也只有一個 MessageQueue 對象
        // 不管 new 多少個 Handler,其成員變量 mQueue 對象皆指向此處建立的 mQueue 對象 
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
複製代碼

2.5 ThreadLocal.set

ThreadLocal.javaide

public void set(T value) {
        // 入參 values 是上面新建的 looper 對象
        Thread t = Thread.currentThread();
        // 獲取當前線程的成員變量 threadLocals 
        ThreadLocalMap map = getMap(t);
        // 此時 map == null
        if (map != null)
            map.set(this, value);
        else
            // 走這個分支
            createMap(t, value);
    }

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

    void createMap(Thread t, T firstValue) {
	    // 入參 firstValue 是上面新建的 looper 對象
	    // 建立一個 ThreadlocalMap 對象,並把 looper 存至其中,最後賦值給成員變量 threadLocals
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
複製代碼

ThreadLocal 是一個泛型類,此處泛型爲 Looper,它能實現每一個線程擁有各自的 looper 而不產生競爭的緣由是:它將每一個線程的 looper 存儲到了各自的成員變量 threadLocals 中。函數

2.6 ThreadLocalMap

Thread.java

ThreadLocal.ThreadLocalMap threadLocals = null;
    
    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
	    table = new Entry[INITIAL_CAPACITY];
	    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
	    // 保存 looper 對象
	    table[i] = new Entry(firstKey, firstValue);
	    size = 1;
	    setThreshold(INITIAL_CAPACITY);
    }
複製代碼

ThreadLocalMap 是自定義的一個數據結構,由數組實現,其元素類型爲 Entry。Entry 保存一個鍵值對,key 始終爲ThreadLocal<?> 類型,value 爲 Object 類型。此處 key 即爲 Looper.java 中的靜態變量 sThreadLocals,而 value 爲以前建立的 looper 對象。

2.7 sMainLooper

Looper.java

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // 初始化 sMainLooper,便於經過 new Handler(Looper.getMainLooper()) 方式向主線程發消息
            sMainLooper = myLooper();
        }
    }

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
複製代碼

2.8 ThreadLocal.get

ThreadLocal.java

public T get() {
        Thread t = Thread.currentThread();
        // 獲取當前線程的 threadLocals 對象,此處爲 UI 線程
        ThreadLocalMap map = getMap(t);
        // 已經調用 prepare(false),此處 map 不爲 null
        if (map != null) {
			// 這個 this 就是 sThreadLocal
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                // 此處 value 爲 Object 類型,須要強轉爲 Looper 類型
                T result = (T)e.value;
                // 返回該線程的 looper 對象
                return result;
            }
        }
        return setInitialValue();
    }
複製代碼

至此,UI 線程的 looper 對象已經建立而且保存到了 UI 線程的 threadLocals 對象中,而且賦值給了 Looper.java 的靜態變量 sMainLooper,以便在其它線程中,經過 new Handler(Looper.getMainLooper()) 方式向主線程發消息。接下來看 Handler 的初始化。

三. Handler 初始化

3.1 Handler

Hanlder.java

public Handler() {
        this(null, false);
    }

    public Handler(Callback callback, boolean async) {
        // 省略...
	
        // Handler 在哪一個線程建立,取出來的就是哪一個線程的 looper 
        mLooper = Looper.myLooper();
        // 未調用 Looper.prepare() 的線程,沒法建立 Handler
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 指向建立 looper 時所建立的 MessageQueue 對象
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
複製代碼

沒有 looper 就沒法建立 Handler,一樣建立 Handler 時也能夠爲其指定 looper。

四. Handler 發送消息

4.1 Handler.sendEmptyMessage

Handler.java

public final boolean sendEmptyMessage(int what) {
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        // 從對象池中取出一個 Message 對象
        // 注:Message 對象使用後會被回收進對象池(大小爲50),以便下次複用
        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);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        // mQueue 就是這個 handler 所在線程對應的 looper 對象中的 mQueue 對象
        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);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        // 將 handler 對象賦值給 msg 的 target
        msg.target = this;
        // mAsynchronous 默認是 false
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        // 調用 MessageQueue 的 enqueueMessage() 將消息投入 MessageQueue
        // 注:不想翻譯成消息隊列,由於通常說消息隊列指的是 Linux IPC 方式的一種。
        return queue.enqueueMessage(msg, uptimeMillis);
    }
複製代碼

4.2 MessageQueue.enqueueMessage

MessageQueue.java

// MessageQueue 是由單向鏈表實現的、老是按照 msg.when 升序排序的隊列。
    // 其成員變量 mMessages 表明表頭。
    Message mMessages;

    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;
            // 初始狀態下,mMessages 爲 null
            Message p = mMessages;
            boolean needWake;
            // 若是表頭爲 null 或者新消息的 when 小於表頭的 when,進入這個分支
            if (p == null || when == 0 || when < p.when) {
                // 入參 msg 的下一個節點指向當前表頭,即入參 msg 成爲新的表頭
                msg.next = p;
                // 表明表頭的成員變量從新賦值
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 若是表頭不爲 null 且新消息的 when 大於等於 表頭的 when,則進入這個分支
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                // 遍歷鏈表,找出下一節點爲 null(即表尾) 或者 when 大於等於新消息 when 的節點
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // 若是未找到 when 大於等於新消息 when 的節點,則將 msg 追加到表尾。
                // 不然將 msg 插入到該結點以前
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                // 新消息入隊,須要喚醒,nativePollOnce() 才能返回
                nativeWake(mPtr);
            }
        }
        return true;
    }
複製代碼

MessageQueue 是由單向鏈表實現的,老是按照 msg.when 升序排序的隊列。新發送的消息會插入到發送時間比它晚的消息以前。

五. Handler 處理消息

5.1 Looper.loop()

Looper.java

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // 循環讀取消息並處理,無消息時阻塞。這種寫法是最經常使用的 Linux IO 操做方式。
        for (;;) {
            // 取出一個消息,若沒有消息要處理,則阻塞
            Message msg = queue.next(); // 可能阻塞
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                // 調用 handler 的 dispatchMassage() 分發消息
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
			
            // 回收進對象池
            msg.recycleUnchecked();
        }
    }
複製代碼

5.2 MessageQueue.next()

MessageQueue.java

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 循環
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
			
            // native 層阻塞函數,nextPollTimeoutMillis 爲超時時間,首次循環時值爲0,即直接返回
            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) {
                    // 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) {
                    if (now < msg.when) {
                        // 下一個消息還沒處處理時間,則設置超時時間爲還需等待的時間,進入阻塞狀態.
                        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 須要取走處理,故須要從鏈表中斷開
                        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;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run. Loop and wait some more.
                    // mBlocked 標記爲 true,進入阻塞狀態,有新消息入隊時,會調用 nativeWake() 喚醒
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

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

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }
複製代碼

5.3 Handler.dispatchMessage###

Handler.java

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
	        // 若是 msg 的 callback 不爲 null,則執行 msg 的 callback
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                 // 若是 handler的 callback 不爲 null,則執行 handler的 callback
                if (mCallback.handleMessage(msg)) {
                    // 若是 callback 的 handleMessage() 返回 true,則再也不調用 handler 的 handleMessage()
                    return;
                }
            }
            // 調用 handler 的 handleMessage()
            handleMessage(msg);
        }
    }
複製代碼

六. 總結

  • 一個線程只有一個 looper 和 一個 messageQueue,handler 能夠建立無數個
  • MessageQueue 是單向鏈表實現的,新消息入隊時,會根據 when 找到合適的位置並插入(即老是按照 msg.when 升序)
  • Message 運用了對象池技術,可經過 obtain()、recycle() 獲取和回收消息
  • 沒有消息處理時,線程會被掛起,直到有新消息時纔會被喚醒執行,底層是經過 IO 多路複用機制中的 epoll 實現的,詳見 MIUI 系統工程師 Gityuan 的 Android消息機制2-Handler(Native層)
  • 除了 Java 層有消息須要處理,Native 層也有本身的消息須要處理,兩者是獨立的,只不過共用了 Native 層的 MessageQueue 和阻塞喚醒機制。因爲消息處理流程老是先處理 Native Message,再處理 Native Request,最後處理 Java Message,因此有時候 Java 層消息不多,但響應時間卻較長:

圖片來自 MIUI 系統工程師 Gityuan 的 Android消息機制2-Handler(Native層)

相關文章
相關標籤/搜索