一起精讀源代碼系列(2) Android Handler源碼分析

做Android研發(fā)的同學(xué)不管是面試還是平時的工作中一定會遇到各種Handler相關(guān)的問題,這一篇我們就通過源代碼一起探討一下Android的Handler機(jī)制。本文的分析基于Android8.0源碼。
1、一個小問題,有代碼如下。一個函數(shù)延遲2秒執(zhí)行,另一個函數(shù)延遲3秒執(zhí)行,其中第一個函數(shù)執(zhí)行了3秒,請問第二個函數(shù)會在什么時候執(zhí)行。

        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 1:
                        methodOne();
                        break;
                    case 2:
                        methodTwo();
                        break;
                    default:
                        break;
                }
            }
        };
        handler.sendEmptyMessageDelayed(1, 2000);
        handler.sendEmptyMessageDelayed(2, 3000);

    private void methodTwo() {
        System.out.println("methodTwo");
    }

    private void methodOne() {
        System.out.println("methodOne");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("methodOneFinish");
    }

帶著這個問題,開始本文分析。
先看構(gòu)造函數(shù)

    public Handler() {
        this(null, false);
    }
    public Handler(Looper looper) {
        this(looper, null, false);
    }
    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
    public Handler(boolean async) {
        this(null, async);
    }
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            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());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

默認(rèn)構(gòu)造函數(shù)callback為null,async為false,async如果為true,則處理程序?qū)⒄{(diào)用Message#setAsynchronous,looper就是Looper.myLooper()的返回值,是從ThreadLocal中g(shù)et到的一個值,也就是如下代碼

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

跟蹤到ThreadLocal的get函數(shù)

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

第一行,獲取當(dāng)前線程;第二行,通過當(dāng)前線程獲取ThreadLocalMap實(shí)例。我們跟蹤進(jìn)去看一下

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

進(jìn)入Thread類,發(fā)現(xiàn)t.threadLocals指的是ThreadLocal.ThreadLocalMap,初始值為null。
所以會調(diào)用setInitialValue函數(shù)

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

value返回為null,map初始值也為null,所以會進(jìn)createMap

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

這里的泛型T是ThreadLocal包裹的泛型對象,在Looper類中,ThreadLocal包裹的泛型對象就是Looper。那這段代碼的意思就是實(shí)例化了一個ThreadLocalMap,key為ThreadLocal類,value為Looper對象。ThreadLocalMap類在此不做詳細(xì)說明,就理解為一個類似于鍵值對存儲的數(shù)據(jù)結(jié)構(gòu)就行。根據(jù)上面的代碼分析,這里的value為null,所以最后return的也是null。
顯而易見,如果myLooper返回值是這樣的話Handler就無法工作了。所以我們要提到另外一個重要的函數(shù),Looper.prepare

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

這里sThreadLocal.get() 不能有值,因此,每個線程只能調(diào)用一次Looper.prepare函數(shù)。下面一行是ThreadLocal的set的過程

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

這時候value就是new的一個Looper對象了,這樣就做到了以ThreadLocal為key,Looper為value的鍵值對存進(jìn)了ThreadLocalMap,ThreadLocalMap與當(dāng)前線程進(jìn)行了綁定。這樣就做到了從當(dāng)前線程中獲取Looper。
所以在使用Handler之前,必須執(zhí)行Looper.prepare,之所以我們平時在主線程中new Handler不需要執(zhí)行,是因?yàn)樵贏ctivityThread中已經(jīng)幫我們執(zhí)行了。參看ActivityThread第6525行。

        Looper.prepareMainLooper();
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

到此為止,構(gòu)造函數(shù)中Looper的分析過程已完成,下面我們需要看另一個重要類的分析,也就是 mQueue = mLooper.mQueue這行代碼的分析。mQuene是Handler類中的一個成員變量,類名為MessageQueue,根據(jù)英文翻譯,可以理解為消息隊(duì)列,但內(nèi)部的數(shù)據(jù)結(jié)構(gòu)并不是隊(duì)列,內(nèi)部存儲的都是Message,從代碼層面來講其實(shí)是單鏈表的結(jié)構(gòu)。mQuene來源于mLooper的成員變量,初始化的過程在

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

Message類有幾個重要的屬性,在這里先大概列出來

    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    long when;
    Bundle data;
    Handler target;
    Message next;

前面打好了基礎(chǔ),接下來,我們就可以開始看整個Handler的運(yùn)行機(jī)制了。以文章開頭的問題為例,我們來看下代碼的執(zhí)行過程。首先是new Handler,這個上文已經(jīng)分析過了,然后里面有一個handleMessage的覆蓋,我們看下這里是怎么回調(diào)過來的。
首先來到ActivityThread的第6541行

        Looper.loop();

這個loop函數(shù)有50多行,我們從頭分析。前幾行是這樣的

        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;

通過throw的異??梢钥闯?,在線程中執(zhí)行l(wèi)oop之前必須先prepare。
然后拿到了MessageQuene實(shí)例。
再下面幾行,啟動了一個無限for循環(huán)

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

目的就是不斷的從quene中取message,message為null的時候退出循環(huán)。那我們需要看下next函數(shù)的具體實(shí)現(xiàn)。

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

            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) {

可以看到,它也有一個無限for循環(huán)。然后有一個nativePollOnce函數(shù)的調(diào)用,這是一個native的函數(shù),也就是c++的實(shí)現(xiàn)。主要功能是等待,直到有下一條消息為止。至于為什么死循環(huán)不會導(dǎo)致應(yīng)用卡死(也就是ANR),這個我們稍后再討論,先繼續(xù)往下看??梢钥吹絪ynchronized關(guān)鍵字修飾了this,可以看到取消息的過程是線程安全的。定義了兩個參數(shù)prevMsg表示之前那條消息,msg表示下一條消息。

                    // 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) {
                        // 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 {

下面一個do while循環(huán),從鏈表中尾部取出消息。當(dāng)now小于msg.when,也就是當(dāng)消息的執(zhí)行之間大于現(xiàn)在的時間(這個when是開發(fā)者設(shè)定的消息執(zhí)行時間點(diǎn),后面也會解釋),下一條message還沒準(zhǔn)備好,所以需要計(jì)算出還需要多久去喚醒這條消息,也就是nextPollTimeoutMillis 這個參數(shù)。如果當(dāng)前時間大于next消息的執(zhí)行時間,則執(zhí)行如下代碼

                        // 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 {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

這就是獲取消息的過程。首先mBlocked設(shè)置為false,阻塞的tag先關(guān)閉。如果當(dāng)前一條消息不為null,則把鏈表的最后一條消息賦值給到prevMsg的next消息,否則把鏈表的最后一條消息賦值給mMessages這個成員變量。最后把鏈表的最后一條消息刪除并且把當(dāng)前取到的msg置為正在使用,把msg給return給調(diào)用者。再往后的源碼就是IdelHandler的相關(guān)使用了,我們后面再具體分析。分析到這,就有幾個結(jié)論了,我分別列舉出來
1、MessageQuene是單鏈表。
2、每次取出的是鏈表尾部的消息,當(dāng)這條消息未到執(zhí)行時間,會計(jì)算出喚醒時間進(jìn)入nativePollOnce等待喚醒,否則直接取出msg返回并從鏈表中刪除這條msg。
3、當(dāng)鏈表中沒有msg的時候,nextPollTimeoutMillis為-1,持續(xù)nativePollOnce等待喚醒。

分析完了取消息的過程,繼續(xù)回到之前的loop函數(shù)。

            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

msg.target指的是handler對象,也就是說looper從MessageQuene取到消息之后會進(jìn)入到handler的dispatchMessage流程。

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

我們假定不設(shè)置callBack,則最終進(jìn)入到了handleMessage,這就是new Handler里面handleMessage函數(shù)的執(zhí)行過程。
接下去是sendMessage的過程分析。通過跟蹤sendMessage相關(guān)的源代碼,可以發(fā)現(xiàn)不管是sendMessage還是sendEmptyMessage都最終會進(jìn)入到如下函數(shù)

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

其中SystemClock.uptimeMillis() 是表示系統(tǒng)開機(jī)到當(dāng)前的時間總數(shù),單位是毫秒,但是,當(dāng)系統(tǒng)進(jìn)入深度睡眠(CPU休眠、屏幕休眠、設(shè)備等待外部輸入)時間就會停止,但是不會受到時鐘縮放、空閑或者其他節(jié)能機(jī)制的影響。官方解釋如下

    /**
     * Returns milliseconds since boot, not counting time spent in deep sleep.
     *
     * @return milliseconds of non-sleep uptime since boot.
     */
    @CriticalNative
    native public static long uptimeMillis();

下面的enqueueMessage函數(shù)就是MessageQuene中Message的添加過程。
先截取一些代碼看一下

        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;
            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 {

又看到了synchronized關(guān)鍵字,所以enqueueMessage也是線程安全的。通過精讀上方代碼,我們可以得出如下結(jié)論
1、p==null表示第一次sendMessage,則鏈表中只有這個消息。
2、when==0表示handler執(zhí)行了sendMessageAtFrontOfQueue這個函數(shù),官方文檔標(biāo)注的是說把消息放在消息隊(duì)列的最前方,在單鏈表中指的是放到鏈表的尾部。
3、when<p.when指的是執(zhí)行時間小于鏈表尾部消息的執(zhí)行時間,則把這條消息置于鏈表尾部。
當(dāng)p!=null&&when>0&&when>p.when的時候的執(zhí)行流程如下

                // 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.
                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;

啟動一個死循環(huán),直到p==null或者when<p.when的時候退出,目的是為了找到鏈表中when值大于當(dāng)前when的消息。然后把p賦值給msg.next,msg賦值給pre.next。很顯然,這就是往鏈表中插入了一個元素。when值越小,越接近鏈表尾部。分析至此,我們又可以得出結(jié)論了。
1、結(jié)論前提:p!=null&&when>0&&when>p.when
2、MessageQuene中每次進(jìn)入新消息,都需要根據(jù)msg.when進(jìn)行排序,msg.when越小,越接近鏈表尾部。
至此為止,源碼層面的Handler機(jī)制全部分析完畢。
接下來,我們解答文出現(xiàn)的幾個問題:
1、為什么要有l(wèi)ooper死循環(huán)。
因?yàn)镴ava的Main函數(shù)執(zhí)行完就退出了。對于Android這樣的GUI程序,肯定不能執(zhí)行完就退出,所以引入了死循環(huán),讓線程一直執(zhí)行下去。
1、Looper.loop是一個死循環(huán),為什么不會卡死Android UI,為什么不會導(dǎo)致ANR。
Android 所有的 UI 刷新和生命周期的回調(diào)都是由 Handler消息機(jī)制完成的,就是說 UI 刷新和生命周期的回調(diào)都是依賴 Looper 里面的死循環(huán)完成的。其中,在UI的渲染過程中,會調(diào)用到如下代碼

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

其中有這么一行mHandler.getLooper().getQueue().postSyncBarrier();這就是說渲染view的過程也用到了handler機(jī)制,并且渲染message的優(yōu)先級高于普通message。在Android各大組件的生命周期中,利用了ActivityThread類中的H類來處理,也是Handler機(jī)制。
2、nativePollOnce是什么。
參考linux epoll
3、文章開頭的問題答案是什么。
第二個函數(shù)會等待第一個函數(shù)執(zhí)行完了再執(zhí)行。根據(jù)以上分析,dispatchMessage發(fā)生在loop for循環(huán)中,消息是一條條取出的,msg.when小的排在鏈表尾部,會優(yōu)先被取出然后dispatch。handleMessage執(zhí)行完了之后,才會取下一條消息執(zhí)行。

下一篇預(yù)告:ConcurrentHashMap源碼分析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容