Handler、Looper和MessageQueue機制閱讀筆記

簡介

在Android日常開發(fā)中,Handler的使用是非常高頻的。其類似于一種通信機制,這種機制主要靠Handler、Looper和MessageQueue的協(xié)同合作。
MessageQueue:消息隊列,先進先出,在獲取下一條消息的時候可能因為沒有消息從而阻塞。
Looper:通過ThreadLocal修飾,保證每一個線程都有一個獨立的Looper。工作開始后會處于一個無限循環(huán),不停地從MessageQueue取出消息,然后傳遞給Message指定的Handler處理。
Handler:投遞消息和處理消息,將生成一個新的消息Message,然后交給對應(yīng)Looper中的Message,等待Looper取出的消息進行回調(diào)處理。

Looper

    //用于管理不同線程下不同的Looper
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //UI線程的Looper,一般在整個進程開始的時候就創(chuàng)建了,后續(xù)可以直接使用
    private static Looper sMainLooper;
    //當前Looper持有的消息隊列
    final MessageQueue mQueue;
    //當前Looper關(guān)聯(lián)的線程
    final Thread mThread;

Looper通過ThreadLocal保證在每一個線程中都有唯一且確認的Looper,內(nèi)部持有一個唯一的消息隊列和創(chuàng)建線程。

    /**
     * 準備Looper
     */
    public static void prepare() {
        prepare(true);//默認允許消息隊列退出
    }

    /**
     * 準備Looper
     * @param quitAllowed true允許MessageQueue通過quit退出,false當quit的時候會拋出異常
     */
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//當前線程已經(jīng)綁定有Looper
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    /**
     * 創(chuàng)建一個新的Looper
     * @param quitAllowed
     */
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//新建一個消息隊列
        mThread = Thread.currentThread();//綁定當前線程
    }

Looper不存在顯示的構(gòu)造方法,通過靜態(tài)方法prepare可以在當前線程中構(gòu)建Looper,但是不允許重復prepare,一個線程中只允許準備一個Looper

    /**
     * 通過當前調(diào)用線程獲得對應(yīng)的Looper
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    /**
     * Looper開始工作
     */
    public static void loop() {
        final Looper me = myLooper();//獲得當前調(diào)用線程的Looper
        if (me == null) {//必須先創(chuàng)建Looper
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;//獲得Looper對應(yīng)的消息隊列
        //...
        for (;;) {//無限循環(huán)
            Message msg = queue.next(); //當隊列中沒有數(shù)據(jù)或者下一條消息需要延時的時候可能會阻塞,next()內(nèi)部本身就是一個死循環(huán)
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //...
            //成功取出下一條Message
            try {
                //將消息回調(diào)到Handler中處理
                msg.target.dispatchMessage(msg);
            } finally {
                //...
            }
            //...
            msg.recycleUnchecked();//當前消息分發(fā)處理完成,回收當前消息,用于后期復用
        }
    }

當一個新的線程準備好了Looper之后,要讓Looper開始工作需要loop(),在一個無限循環(huán)中不停地從Looper中的消息隊列中取出消息并且回調(diào)到對應(yīng)的Handler中處理,在這個過程中如果取不出消息則會阻塞,有消息可以獲得的時候會喚起。

Handler

    /**
     * 默認構(gòu)造函數(shù),會自動關(guān)聯(lián)當前調(diào)用線程的Looper
     * 如果當前線程沒有創(chuàng)建Looper,會拋出異常
     */
    public Handler() {
        this(null, false);
    }

    /**
     * 會自動關(guān)聯(lián)當前調(diào)用線程的Looper,并且指定消息的回調(diào)處理
     * 如果當前線程沒有創(chuàng)建Looper,會拋出異常
     *
     * @param callback 用于處理消息的回調(diào),可以為null
     */
    public Handler(Handler.Callback callback) {
        this(callback, false);
    }

    /**
     * 使用特定的Looper,并且指定消息的回調(diào)處理
     *
     * @param looper 必須為非空的Looper
     * @param callback 用于處理消息的回調(diào),可以為null
     */
    public Handler(Looper looper, Handler.Callback callback) {
        this(looper, callback, false);
    }

    /**
     * 使用特定的Looper
     *
     * @param looper 必須為非空的Looper
     */
    public Handler(Looper looper) {
        this(looper, null, false);
    }

    /**
     * 會自動關(guān)聯(lián)當前調(diào)用線程的Looper,并且指定消息的回調(diào)處理
     * 如果當前線程沒有創(chuàng)建Looper,會拋出異常
     *
     * @param callback 用于處理消息的回調(diào),可以為null
     * @param async 在將消息推入消息隊列之前,會通過Message的setAsynchronous設(shè)置Message的屬性
     *
     * @hide
     */
    public Handler(Handler.Callback callback, boolean async) {
        mLooper = Looper.myLooper();//關(guān)聯(lián)當前調(diào)用線程的Looper
        if (mLooper == null) {//要求當前線程必須初始化一個Looper
            throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//綁定Looper的消息隊列
        mCallback = callback;
        mAsynchronous = async;
    }

    /**
     * 使用特定的Looper,并且指定消息的回調(diào)處理
     * 
     *
     * @param looper 必須為非空的Looper
     * @param callback 用于處理消息的回調(diào),可以為null
     * @param async 在將消息推入消息隊列之前,會通過Message的setAsynchronous設(shè)置Message的屬性,后續(xù)通過這個Handler發(fā)送的消息都是異步消息,如果消息隊列設(shè)置了同步屏障,那么只會執(zhí)行這些消息,否則就和普通消息沒有什么區(qū)別
     *
     * @hide
     */
    public Handler(Looper looper, Handler.Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

這里有消息的異步屬性,不過后面會看到正常使用的時候并沒有什么作用。
重點是可以看到Handler在構(gòu)建的時候必須關(guān)聯(lián)指定的Looper,同時會綁定Looper里面對應(yīng)的MessageQueue,這樣關(guān)聯(lián)關(guān)系就構(gòu)建完成。

    /**
     * 發(fā)送一條延時處理的消息,并且指定回調(diào)
     * @param r 當前消息的回調(diào)處理
     * @param delayMillis 消息的處理延時
     * @return 當前消息是否發(fā)送成功
     */
    public final boolean postDelayed(Runnable r, long delayMillis)
    {
        //生成一個有指定回調(diào)的消息,并且在指定延時后發(fā)送消息
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

    /**
     * 獲得一條消息,并且指定回調(diào)
     * @param r 當前消息的回調(diào)處理
     * @return Message
     */
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();//嘗試復用一個Message
        m.callback = r;//指定callback為對應(yīng)的Runnable
        return m;
    }

    /**
     * 發(fā)送一條延時處理的消息,并且指定回調(diào)
     * @param msg 需要發(fā)送的消息
     * @param delayMillis 消息的處理延時
     * @return 當前消息是否發(fā)送成功
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {//處理延時必須大于等于0
            delayMillis = 0;
        }
        //發(fā)送一條在當前時間+延時時間的時間點處理的消息
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    /**
     * 將消息放入指定消息隊列當中
     * @param msg 需要放入到消息隊列的消息
     * @param uptimeMillis 消息需要被回調(diào)的時間
     * @return true表示消息放入到消息隊列成功,false失敗
     */
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;//獲得當前Looper關(guān)聯(lián)的消息隊列
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        //將消息放入當前Looper關(guān)聯(lián)的消息隊列
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    /**
     * 將消息放入指定消息隊列當中
     * @param queue 需要放入的消息隊列
     * @param msg 需要放入到消息隊列中的消息
     * @param uptimeMillis 當前消息回調(diào)的時間
     * @return true表示消息放入到消息隊列成功,false失敗
     */
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//指定當前消息的target為當前發(fā)送的Handler
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);//調(diào)用消息隊列的方法將消息放入隊列當中
    }

可以看到,Handler發(fā)送消息實際上就是把Message放入到對應(yīng)Looper的MessageQueue中,然后工作中的Looper會在指定的時間取出當前Message,然后再次回調(diào)到Handler當中。

    /**
     * 在Looper的loop執(zhí)行的線程中進行的回調(diào)
     * 用于處理消息
     * @param msg 需要處理的消息
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//如果消息是直接通過post(Runnable)的類似方式發(fā)送到隊列中的
            handleCallback(msg);
        } else {
            if (mCallback != null) {//Handler在創(chuàng)建的時候如果指定了Callback,優(yōu)先回調(diào)到Callback中
                if (mCallback.handleMessage(msg)) {//Callback處理消息完成是否結(jié)束本次回調(diào)
                    return;
                }
            }
            //如果是直接通過sendMessage之類的方式發(fā)送到隊列中,并且在構(gòu)建Handler的時候沒有指定Callback的時候
            //回調(diào)Handler中的方法
            handleMessage(msg);
        }
    }

    /**
     * 回調(diào)消息在發(fā)送之前設(shè)置的Runnable
     * @param message 當前需要處理的消息
     */
    private static void handleCallback(Message message) {
        message.callback.run();
    }

    /**
     * 對于普通的sendMessage...之類的消息
     * 可以通過直接復寫該方法從而進行統(tǒng)一的消息處理
     * @param msg 需要處理的消息
     */
    public void handleMessage(Message msg) {
    }

當Looper成功獲得對應(yīng)Handler的消息的時候,會將消息在Looper所在的線程中交由Handler處理。優(yōu)先級如下:
1.Message有指定Runnable,回調(diào)Runnable,消息處理結(jié)束,否則2
2.Handler在構(gòu)建的時候指定Callback,回調(diào)Callback,如果Callback返回true,消息處理結(jié)束,否則繼續(xù)3
3.否則回調(diào)Handler中的handlerMessage方法,這個一般在開發(fā)中用的會多一些
除此之外可以看到,如果想要Handler的處理方法運行在非UI線程上,只需要指定非UI線程的Looper即可,比方說官方提供的HandlerThread和Handler組合的模式。

    HandlerThread handlerThread = new HandlerThread("msg_handler");
    handlerThread.start();
    Handler.Callback callback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //在HandlerThread的線程中做些什么。。。
            //在handleMessage之前處理
            return false;
        }
    };
    Handler handler = new Handler(handlerThread.getLooper(), callback){
        @Override
        public void handleMessage(Message msg) {
            //在Callback執(zhí)行完畢之后,在HandlerThread的線程中繼續(xù)做些什么。。。
        }
    };
    handler.sendEmptyMessage(0);

通過上面的這種方法就可以讓Handler在子線程中處理消息的回調(diào)。

Message

    //復用池隊列數(shù)據(jù)變化鎖,類似讀寫鎖
    private static final Object sPoolSync = new Object();
    //當前復用池隊列的頭指針,或者說隊首消息
    private static Message sPool;
    //當前消息在消息鏈表中的下一條消息
    Message next;
    //當前復用池的大小
    private static int sPoolSize = 0;
    //復用池隊列的最大長度,超過不再接受舊的消息
    private static final int MAX_POOL_SIZE = 50;

    /**
     * 從復用池隊列中嘗試復用已有的Message
     * 如果沒有則新建
     * @return 可用的Message
     */
    public static Message obtain() {
        synchronized (sPoolSync) {//復用池隊列數(shù)據(jù)變化鎖
            if (sPool != null) {
                Message m = sPool;//獲得隊首元素
                //將隊首元素移出隊列
                sPool = m.next;//當前隊列頭指針指向第二個元素
                m.next = null;//斷開隊首元素指向第二個元素的指針,從而從隊列中移出完成
                m.flags = 0; // clear in-use flag
                sPoolSize--;//復用池隊列數(shù)量-1
                return m;//復用當前復用池隊首的消息
            }
        }
        //如果復用池沒有可以復用的消息,新建消息
        return new Message();
    }

    /**
     * 回收當前消息并且放在復用池鏈表的頭部
     * 用于后期的復用
     */
    void recycleUnchecked() {
        //... 首先清空數(shù)據(jù)
        synchronized (sPoolSync) {//復用池隊列數(shù)據(jù)變化鎖
            if (sPoolSize < MAX_POOL_SIZE) {//當前復用池中的數(shù)據(jù)還沒到設(shè)置的最大值
                //將當前消息插入到復用池的頭部
                next = sPool;//當前消息指向復用池隊列的頭指針
                sPool = this;//當前消息作為復用池隊列的頭指針,插入完成
                sPoolSize++;//當前復用池中的消息數(shù)量+1
            }
        }
    }

消息采用復用機制,通過一個后進先出的隊列來進行管理,隊列默認最大大小為50個Message。
在Android中Handler使用還是比較頻繁,通過Message的復用還是可以減少頻繁的內(nèi)存分配和回收。
Message在Looper中回調(diào)到Handler處理完成之后會自動回收,所以平時在使用的過程中不應(yīng)該手動進行回收。

MessageQueue

    /**
     * 將消息放入消息隊列當中
     * 消息隊列會按照消息的執(zhí)行時刻進行排列,執(zhí)行時刻早的位于隊列的前面
     * @param msg 需要放入的消息
     * @param when 當前消息設(shè)置的執(zhí)行時刻
     * @return true表示放入消息隊列成功,false在MessageQueue已經(jīng)quit的情況下會返回,表示當前MessageQueue不可用
     */
    boolean enqueueMessage(Message msg, long when) {
        //...一些檢查
        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;//標記當前消息的執(zhí)行時刻
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                //1.當前消息隊列為空
                //2.當前消息需要立刻執(zhí)行
                //3.當前消息執(zhí)行時刻比隊首消息還要早
                //以上三種情況需要將當前消息插入到消息隊列的頭部即隊首
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;//如果之前next處于阻塞狀態(tài),需要嘗試喚起隊列
            } else {//在非隊列頭部的地方插入消息
                //如果之前next處于阻塞狀態(tài),并且當前消息為異步消息,需要嘗試喚起隊列
                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;
                prev.next = msg;
            }

            if (needWake) {//如果需要喚醒隊列,會嘗試喚起隊列,則next()會繼續(xù)執(zhí)行
                nativeWake(mPtr);
            }
        }
        return true;
    }

    /**
     * 從消息隊列中獲取下一條用于回調(diào)的消息
     * @return 用于回調(diào)的消息
     */
    Message next() {
        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);//阻塞指定的時間,nextPollTimeoutMillis指定下一次喚醒時間間隔

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();//當前時刻
                Message prevMsg = null;
                Message msg = mMessages;//標記消息隊列隊首消息
                //當target為null的時候,這種場景只能通過postSyncBarrier實現(xiàn)
                if (msg != null && msg.target == null) {
                    //當通過postSyncBarrier放入的消息之后
                    //只會執(zhí)行指定了Asynchronous屬性的Handler發(fā)送的消息或者手動設(shè)置為Asynchronous的消息
                    //這種消息也就是相當于一個同步屏障的作用,導致MessageQueue只會取出異步消息
                    //從而導致Handler不會執(zhí)行同步的消息,而只會執(zhí)行異步的消息
                    //默認情況下這種消息不會自動移除,除非調(diào)用removeSyncBarrier
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {//當前消息隊列不為空
                    if (now < msg.when) {//當前還沒有到最快執(zhí)行的消息的執(zhí)行時刻
                        //延時指定的時間
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {//當前消息可以執(zhí)行
                        mBlocked = false;
                        if (prevMsg != null) {//當前消息為異步消息,此時可能是隊列中間的某一個消息
                            prevMsg.next = msg.next;//將當前消息前面的鏈表和后面的鏈表連在一起
                        } else {
                            mMessages = msg.next;//鏈表頭指針變成隊列中第二個消息
                        }
                        msg.next = null;//斷開當前消息在隊列中的指針,從而成功將當前消息從鏈表中移出
                        msg.markInUse();//標記的當前消息在使用中
                        return msg;//返回隊首消息
                    }
                } else {
                    //消息隊列沒有滿足要求的消息或者消息隊列為空
                    nextPollTimeoutMillis = -1;
                }

                if (mQuitting) {
                    dispose();
                    return null;
                }

                //執(zhí)行到這里,說明當前消息隊列沒有可以執(zhí)行的消息,可能沒有到執(zhí)行時間或者隊列中就沒有消息
                //接著嘗試進行當前消息隊列空閑的監(jiān)聽回調(diào),可以手動通過addIdleHandler添加監(jiān)聽回調(diào)

                //空閑回調(diào)在一次next的過程中只能回調(diào)一次
                //如果當前隊列為空或者還沒有到下一條消息的執(zhí)行時刻,即處于空閑狀態(tài)
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    //標記回調(diào)的數(shù)量,從而也意味著當前準備開始回調(diào)
                    //后續(xù)pendingIdleHandlerCount都不會小于0,也就不會再次回調(diào)
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {//當前沒有設(shè)置監(jiān)聽回調(diào)
                    mBlocked = true;//標記需要阻塞,因為當前隊列空閑
                    continue;//繼續(xù)循環(huán),從而進入到nativePollOnce進行阻塞
                }

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

            // 嘗試進行空閑回調(diào)
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final MessageQueue.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) {//如果空閑回調(diào)處理返回false,這里會自動移除回調(diào)
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // 標記需要進行空閑回調(diào)的數(shù)量為0,這樣后續(xù)循環(huán)不會再次進行空閑回調(diào)
            pendingIdleHandlerCount = 0;

            //如果進行了空閑回調(diào),那么也許會有新消息的到來,則下一次進入循環(huán)先不阻塞
            nextPollTimeoutMillis = 0;
        }
    }

MessageQueue中維持著一個按照消息執(zhí)行時刻排序的列表,插入消息的時候會按照時刻所處位置進行插入。
Looper在loop()中不停地循環(huán)MessageQueue的next()方法,next()方法會嘗試將第一條可用消息提供。
可以看到異步的消息會優(yōu)先執(zhí)行,通常使用下的消息都會有對應(yīng)的target,除非通過MessageQueue的postSyncBarrier()設(shè)置一個同步屏障,這樣會導致后續(xù)只執(zhí)行異步的消息。

空閑回調(diào)

1.如果消息隊列為空,首先會嘗試回調(diào)空閑Handler,然后再次嘗試獲取新消息,如果還沒有新消息,則會一直阻塞,等待enqueMessage中插入消息的喚起。
2.如果消息隊列不為空,但是第一條消息還沒有到指定的執(zhí)行時刻,首先會嘗試回調(diào)空閑Handler,然后再次嘗試獲取新消息,如果還沒有新消息,則會阻塞到第一條消息執(zhí)行的時刻。
3.每一次next()只能獲得一條消息,并且對應(yīng)的空閑回調(diào)只有一次。(使用例子可以看Glide對于activeResource弱內(nèi)存緩存的回收處理)

總結(jié)

如果把這個工作比作是一個工廠出品一個零器件。
Handler就是工廠大門兩端的加工人員,在大門入口負責把新來的零件放到循環(huán)工作的履帶,然后在大門出口負責最后把零件包裝一下然后送出去。
MessageQueue就是這個履帶,零件在履帶上面按照它們的執(zhí)行循序排列。
Looper就是履帶上面的自動機器手,在特定的時刻將履帶上面對應(yīng)的零件交給出口的Handler。

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

相關(guān)閱讀更多精彩內(nèi)容

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