Android Handler學習筆記(一)

 這篇筆記主要參考瞭如下博客的文章:web

Android Handler機制
Android 消息機制之MessageQueueshell

Handler通訊機制的工做原理

Handler的做用

    爲何使用Handler?數組

  1. 在Android中,若是在主線程中進行耗時操做,則容易出現ANR,新版本的Android系統中,若是在主線程中進行耗時操做,還會拋出異常。所以須要將耗時操做放在子線程中進行。
  2. 在子線程中進行完耗時操做以後,若是須要對UI進行修改,則須要使用Handler將須要更新UI的操做包裝成一個Message發送到主線程,而後在主線程中進行更新UI的操做。

    在上面的描述中,涉及到一些問題,以下:安全

  1. ANR:Application Not Responding,出現這個問題以後APP會彈出提示框詢問用戶繼續等待或者結束應用。通常狀況下在主線程進行耗時操做會出現這個問題。
  2. 對於一個子線程來講,在須要操做UI的時候纔會用到Handler,若是子線程不用操做UI,那麼就不須要Handler,子線程執行完任務結束就能夠了。
  3. 主線程:也就是UI線程,在啓用APP的時候會默認建立。

    綜上所示,Handler通常用於多線程中,將工做線程中須要更新UI的操做信息傳遞到主線程中,從而實現工做線程對UI的更新處理,最終實現異步消息的處理。使用Handler的目的,也是爲了在多個線程併發更新UI的同時,保證線程安全。bash

相關概念

    和Handler相關的概念主要包括Handler,Message,Message Queue,Looper,相關概念以下:網絡

概念 定義 做用 備註
主線程 當應用程序第一次啓動的時候,會同時自動開啓一條主線程 處理和UI相關的事件 主線程和子線程通訊的媒介=Handler
子線程 人爲手動開啓的線程 執行耗時操做(如網絡請求等) 與主線程經過Handler通訊
Message 線程間通訊的數據單元 存儲需操做的通訊信息 通常在子線程中建立
Message Queue 一種數據結構(先進先出) 存儲Handler發送過來的消息 存在於主線程中
處理者(Handler) 主線程與子線程通訊的媒介,線程消息的主要處理者 添加消息(Message)到消息隊列(Message Queue);
處理循環器(Lopper)分派過來的消息(Message)
/
循環器(Looper) 消息隊列(Message Queue)與處理者(Handler)的通訊媒介 負責消息循環,也就是:
1.消息獲取:循環取出消息隊列(Message Queue)的消息(Message);
2.消息分發:將取出的消息(Message)發送給對應的處理者(Handler)
  • 每一個線程中只能擁有一個Looper
  • 1個Looper可綁定多個線程的Handler
  • 即多個線程可往一個Looper所持有的Message Queue中發送消息,提供了線程間通訊的可能。
  • 使用方式

        Handler的使用方式因發送消息到消息隊列的方式不一樣而不一樣,共分爲兩種:數據結構

    1. Handler.sendMessage(Message)
    2. Handler.post(Runnable)

    工做原理

        Handler機制的工做流程主要包括四個步驟:多線程

    • 異步通訊準備
    • 消息發送
    • 消息循環
    • 消息處理

        具體以下表所示:併發

    步驟 具體描述 備註
    1.異步通訊準備 在主線程中建立:
  • 循環器對象(Looper)
  • 消息隊列對象(Message Queue)
  • Handler對象
  • Looper,MessageQueue均屬於主線程
  • 建立Message Queue後,Looper則自動進入消息循環體
  • 此時,Handler自動綁定了主線程的Looper,Message Queue
  • 2.消息入隊 工做線程(子線程)經過Handler發送消息(Message)到消息隊列(Message Queue)中 該消息內容 = 工做線程對UI的操做
    3.消息循環
  • 消息出隊:Looper循環取出消息隊列(MessageQueue)中的消息(Message)
  • 消息分發:Looper將取出的消息(Message)分發給建立該消息的處理者(Handler)
  • 在消息循環過程當中,若消息隊列爲空,則線程阻塞
    4.消息處理
  • 處理者(Handler)接收循環器(Looper)發送過來的消息(Message)
  • 處理者(Handler)根據消息(Message)進行UI操做
  • /

        Handler的工做流程以下圖所示:app

    Handler的工做流程

        Handler的工做流程示意圖以下圖所示:

    Handler工做流程示意圖

    Handler機制的核心類

        Handler機制中有三個重要的類,分別是:

    1. 處理器 類(Handler)
    2. 消息隊列 類(Message Queue)
    3. 循環器 類(Looper)

        三個重要類的類圖以下所示:

        三個重要的類的具體介紹以下圖所示:

    小結

        至此,咱們已經瞭解了Handler的基本運行機制:

    當一個應用啓動的時候,會同時建立一個主線程或稱爲UI線程,在這個線程中會同同時建立Message QueueLooper。當咱們須要在子線程中進行某一項工做,而且工做完成以後須要操做UI的時候,咱們會建立一個Handler用於接收子線程操做UI的信息。

    執行完上面的操做以後,咱們一般會開啓一個子線程,爲了可以操做UI,咱們須要將上一步建立的Handler對象傳遞到子線程中,當子線程執行完操做以後,會將須要操做UI的信息包裝成一個Message對象,經過Handler.sendMessage()方法將Message添加到Message Queue中。

    因爲Looper是一個死循環,當有消息進入到Message Queue中的時候,會當即從其中取出消息,而後就開始分發消息開始執行。這樣就完成了從子線程向主線程中傳遞消息的操做。

        可是到這裏,仍然有幾個問題須要解決:

    1. 主線程什麼時候建立?
    2. 主線程中什麼時候建立LooperMessage Queue?如何建立?
    3. Looper如何自動進入消息循環體中?
    4. Handler如何實現和LooperMessage Queue的自動綁定?
    5. 消息循環過程當中,若是消息隊列爲空,則線程阻塞,那豈不是主線程就阻塞了?
    6. Message傳遞消息的時候有什麼限制?

    源碼分析

        Handler發送消息分爲兩種方式,分別是Handler.sendMessage()Handler.post(),下面根據不一樣方式進行源碼分析:

    使用Handler.sendMessage()

      1.使用步驟

    //建立Handler
    private Handler mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if(msg.what == 1){
                    mBinding.tvResult.setText(msg.obj.toString());
                }
            }
        };
    //建立消息對象
    Message message = Message.obtain();
    message.what = 0;
    message.obj = this.getClass().getSimpleName();
    
    //在子線程中發送消息
    mThreadHandler.sendMessage(message);
    
    //這裏的子線程我經過繼承Thread實現
    class TestThread extends Thread{
        private Handler mThreadHandler;
    
        TestThread(Handler handler){
            this.mThreadHandler = handler;
        }
        @Override
        public void run() {
            super.run();
            Message message = Message.obtain();
            message.what = 0;
            message.obj = this.getClass().getSimpleName();
            mThreadHandler.sendMessage(message);
        }
    }
    複製代碼

    步驟1 在主線程中經過匿名內部類建立 Handler對象

     在上面的示例中,經過匿名內部類的方式建立了Handler對象,首先會執行Handler無參的構造方法,源碼以下:

    public Handler() {
            this(null, false);
        }
    複製代碼

     能夠看到,在無參的構造方法中,調用了Handler(callback:null,async:false)的構造方法,源碼以下:

    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 " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    複製代碼

     在上面的源碼中,首先咱們須要判斷FIND_POTENTIAL_LEAKS的值是否爲true,這是一個靜態變量,默認爲false,從名字上也能夠理解,這個變量就是用來標記是否要發現隱藏的內存泄露。

     那麼若是這個值爲true,它的判斷方法爲:

    • 首先經過getClass()獲取到這個類運行時真實的Class信息
    • 而後經過isAnonymousClass(),isMemberClass(),isLocalClass()判斷這個類是不是匿名類(就像 new OnClickListener()這種就是匿名類,上面定義Handler的時候也是用的是匿名類),或者是不是成員類(定義在類中類),亦或者是不是局部類(定義在方法中的類)。
    • 若是是上面任意一種類,那麼接下來經過kclass.getModifiers() & Modifirt.STATIC來查看當前類是否使用static修飾(getModifiers()能夠獲取到類的修飾符,使用整數表示,Modifier.STATIC的二進制數爲1000,作與運算,只有getModifiers()返回的整數的二進制數的最高位爲1,最後的結果纔不會爲0)。
    • 知足上面的條件則會提示"The following Handler class should be static or leaks might occur"
       接下來經過Looper.myLooper()綁定當前線程的Looper,而後判斷,若是Looper爲空則會拋出異常。

    Looper.myLooper()方法的源碼以下:

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

    Looper中的sThreadLocal定義以下:

    // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 複製代碼

     從註釋也能夠看出,只有調用了prepare()方法以後,sThreadLocal.get()纔不會返回null

     查看Looper中的源碼也能夠發現,Looper只有一個private的構造方法,也就是說,Looper只能經過prepare()方法進行初始化,源碼以下:

    //Looper構造方法
        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
        
        //prepare()方法
            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));
        }
            /**
         * Initialize the current thread as a looper, marking it as an
         * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } 複製代碼

     從上面的源碼能夠看出,Looper的主要做用就是將當前線程初始化爲循環線程,經過prepareMainLooper()方法能夠將當前線程標記爲應用程序的主循環程序,主循環程序是由Android環境建立的,本身建立的話可使用prepare()函數。

     由此,咱們能夠得出如下結論:

    1. 當一個應用程序啓動時,會啓動一個線程,這個線程是主線程,同時會經過調用Looper中的prepareMainLooper()方法將當前線程標記爲一個不能夠退出的主循環程序。
    2. 在標記的過程當中,經過判斷ThreadLocal sThreadLocalget()方法是否爲空來使得一個線程中只能有一個Looper,若是sThreadLocal.get()方法返回空,那麼就經過Looperprivate的構造方法建立一個Looper,並將建立的對象設置給sThreadLocal中的ThreadLocalMap進行保存。
    3. 在調用Looper的構造方法的時候會建立MessageQueue的對象,此時就已經建立好了LooperMessageQueue了。
    4. 當咱們須要經過Handler進行子線程向主線程傳遞數據的時候,咱們就能夠直接在主線程中建立Handler,而後在Handler的構造方法當中設置當前線程的LooperMessageQueue對象。而後將Handler的實例傳遞給子線程(在外部建立的子線程須要經過這種方式),這樣在子線程中的數據處理完成以後咱們就就能夠調用Handler的方法向MessageQueue中添加Message了。

     下面是Android APP啓動時的部分源碼:

    public static void main(String[] args) {
                ... // 僅貼出關鍵代碼
    
                Looper.prepareMainLooper(); 
                // 1. 爲主線程建立1個Looper對象,同時生成1個消息隊列對象(MessageQueue)
                // 方法邏輯相似Looper.prepare()
                // 注:prepare():爲子線程中建立1個Looper對象
                
                
                ActivityThread thread = new ActivityThread(); 
                // 2. 建立主線程
    
                Looper.loop(); 
                // 3. 自動開啓 消息循環 ->>下面將詳細分析
    
            }
    複製代碼

    步驟一前的隱式操做:消息循環

     經過上面的分析,咱們已經清楚了當APP啓動的時候,系統會幫咱們自動建立一個主線程,同時生成LooperMessageQueue對象,有了這兩個對象以後,Looper就能夠調用loop()方法進行消息循環了,下面是loop()方法的源碼:

    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;
    
            // Make sure the identity of this thread is that of the local process,
            // and keep track of what that identity token actually is.
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
    
            // Allow overriding a threshold with a system prop. e.g.
            // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
            final int thresholdOverride =
                    SystemProperties.getInt("log.looper."
                            + Process.myUid() + "."
                            + Thread.currentThread().getName()
                            + ".slow", 0);
    
            boolean slowDeliveryDetected = false;
    
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                // This must be in a local variable, in case a UI event sets the logger
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                final long traceTag = me.mTraceTag;
                long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
                long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
                if (thresholdOverride > 0) {
                    slowDispatchThresholdMs = thresholdOverride;
                    slowDeliveryThresholdMs = thresholdOverride;
                }
                final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
                final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
    
                final boolean needStartTime = logSlowDelivery || logSlowDispatch;
                final boolean needEndTime = logSlowDispatch;
    
                if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
    
                final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
                final long dispatchEnd;
                try {
                    msg.target.dispatchMessage(msg);
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                if (logSlowDelivery) {
                    if (slowDeliveryDetected) {
                        if ((dispatchStart - msg.when) <= 10) {
                            Slog.w(TAG, "Drained");
                            slowDeliveryDetected = false;
                        }
                    } else {
                        if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                                msg)) {
                            // Once we write a slow delivery log, suppress until the queue drains.
                            slowDeliveryDetected = true;
                        }
                    }
                }
                if (logSlowDispatch) {
                    showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
                }
    
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
    
                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } } 複製代碼

     在上面的方法中,首先會判斷當前線程的Looper對象是否存在,不存在則會拋出異常。也就是說在執行loop()前必須執行prepare()方法,爲此線程建立Looper,不然會拋出異常。

     接下來的兩行代碼經過註釋能夠知道,是確保此線程的身份是本地進程的身份,並跟蹤該身份令牌的實際含義,這兩行代碼的實際意義並非很懂,須要後面解決。

     接下來是一個for循環,在for循環中,首先經過queue.next()方法獲取一個Message對象,註釋中也說明了這個方法可能會阻塞。若是這個Message爲空,那麼就會跳出循環。若是這個Message不爲空,那麼就會執行msg.target.diapatchMessage(msg)方法,將這個Message分發給對應的Handler去處理。以後調用msg.recycleUnchecked()方法釋放msg

    MessageQueue中獲取Message的操做

     經過上面的源碼能夠發現,Looper中是經過MessageQueue中的next()方法來獲取下一個須要操做的Message,下面是MessageQueue中的next()方法的源碼:

    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) {
                        // 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 {
                            // 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;
                    }
    
                    // 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;
                        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;
            }
        }
    複製代碼

     上面的註釋已經說明:若是消息循環已經退出而且被釋放,那麼就直接返回null,這種狀況通常發生在應用程序在退出後仍然嘗試從新啓動不受支持的循環。結合代碼中ptr == 0的時候返回null能夠知道,當ptr == 0的時候表示消息循環已經退出而且被釋放掉了。 mPtr的做用是保存 native層的MessageQueue的引用地址。

     接着定義pendingIdleHandlerCount = -1;表示空閒事件處理IdleHandler的數量爲-1,即尚未處理過任何的IdleHandler。而後定義nextPollTimeoutMillis = 0

     接着一樣進入一個for死循環,這時會調用nativePollOnce(ptr, nextPollTimeoutMillis);,這個方法額的主要做用是設置線程等待多久以後喚醒,時間由nextPollTimeoutMillis來指定。

     接下來進入到同步方法中,正式開始獲取Message。具體操做步驟以下:

    1. 獲取MessageQueue中的第一個Message對象,也就是mMessages,若是mMessages不爲空,而且mMessagestarget屬性爲null則進入以下循環:
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
    複製代碼

     在上面的循環中,能夠看出,就是從第二個Message開始獲取,若是獲取到的Message不爲空而且不是異步操做的Message則繼續循環,也就是說,這個循環條件是獲取到第一個是異步操做的Message或者循環完成則跳出循環。

     在這裏,首先要解釋一下Message的三種狀態,分別是:

    • 障棧,這種狀態是Messagetarget的屬性爲空
    • 同步,這種狀態就是在設置Message時候設置爲同步屬性
    • 異步,這種狀態就是在設置Message時設置爲異步屬性

     同步和異步屬性的差距主要在於障棧會阻礙同步Message的執行,而不會阻礙異步Message的執行,這裏並不意味着異步的Message會在異步線程執行

    1. 繼續返回到MessageQueue中的代碼中,通過第一步的運行,咱們拿到的Message多是如下狀況:
    • null,MessageQueue中已經沒有待處理的Message,或者障棧以後沒有異步的Message
    • 同步的Message,MessageQueue中的第一個Message不是障棧,那次是這裏就是第一個Message
    • 異步的MessageMessageQueue中的第一個Message時障棧,而且在以後的Message中找到了第一個異步的Message

     接着進入到下面的判斷中:

    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 {
        // 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;
        }
    }
    複製代碼

     在上面的源碼中,首先判斷獲取的Message是否爲空,不爲空則繼續執行以下的操做:

    • 判斷是否已經到達當前Message的執行時間(經過nowmsg.when)進行判斷,若是沒有到達當前線程的執行時間,則設置線程須要阻塞的時間,這個時間最大爲int的最大值。
    • 若是已經達到了當前Message執行的時間,那麼就設置mBlocked = false,也就是不須要阻塞,而後判斷prevMsg是否爲空,不爲空則說明當前MessageQueue的第一個Message是障棧,此時prevMsg是障棧以後第一個異步Message以前的msg,這樣將prevMsgnext指向當前Message的下一個Message便可。若是prevMsg爲空,那麼就直接設置MessageQueue的第一個Message爲當前Message的下一個Message便可。而後設置當前Messagenext爲空,這樣就將當前的MessageMessageQueue中脫離出來了。而後標記當前的Message正在使用中,並返回。
    1. 當前Message爲空,設置nextPollTimeoutMillis = -1;表示沒有更多的Message了。
    2. 接着,當沒有更多的Message須要處理,或者當前的Message尚未到執行時間的話,會查看是否有退出的消息須要處理,若是有,就執行退出的消息:
    // Process the quit message now that all pending messages have been handled.
    if (mQuitting) {
        dispose();
        return null;
    }
    private void dispose() {
        if (mPtr != 0) {
            nativeDestroy(mPtr);
            mPtr = 0;
        }
    }
    複製代碼

     從上面的dispose()方法中也能夠看出,當MessageQueue退出以後,mPtr的值會被設置爲0,表示當前MessageQueue已經退出。

    1. 若是MessageQueue不須要退出,那麼判斷當前MessageQueue是不是第一次空閒,判斷的依據是pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when),pendingIdleHandlerCount的初始賦值爲-1,而且MessageQueue中沒有要處理的Message,或者當前要處理的Message的時間尚未到,那麼此時就獲取須要處理的IdleHandler的數量.代碼以下:
    if (pendingIdleHandlerCount < 0
            && (mMessages == null || now < mMessages.when)) {
        endingIdleHandlerCount = mIdleHandlers.size();
    }
    複製代碼
    1. 接着判斷是否有須要處理的IdleHandler,若是沒有則設置須要阻塞(由於程序執行到這裏,說明已經沒有待處理的Message,或者當前須要處理的Message的時間還沒到,因此線程須要阻塞,等待下一個事件到來),而後跳出循環,開始執行下一次循環。代碼以下:
    if (pendingIdleHandlerCount <= 0) {
        // No idle handlers to run.  Loop and wait some more.
        mBlocked = true;
        continue;
    }
    複製代碼
    1. 接着判斷須要處理的IdleHandlers數組是否爲空,爲空則會建立一個數組,數組的最大值爲須要處理的IdleHandler的數量和4中較大的一個。而後將存放須要處理的IdleHandler的列表中的數據複製到數組中。
    if (mPendingIdleHandlers == null) {
        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
    }
    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
    複製代碼
    1. 接下來就會經過循環遍歷數組中的IdleHandler來執行其中的操做,具體代碼以下:
    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);
            }
        }
    }
    複製代碼

     能夠看到具體的執行過程也是每次從數組中取出一個IdleHandler,而後執行其中的queueIdle()方法,最後執行完成後根據返回的結果,若是執行完成後不須要繼續將這個IdleHandler保持起來,那麼就從列表中移除它,不然不移除。

    1. 接下來從新賦值pendingIdleHandlerCountnextPollTimeoutMillis的值。

     設置pendingIdleHandlerCount = 0主要是爲了保證IdleHandler只會執行一次。而之因此要設置nextPollTimeoutMillis = 0是爲了防止在執行IdleHandler的時候添加了新的信息,這樣是爲了可以當即再次查找MessageQueue中的Message(之因此須要這樣是由於在上面獲取Message的時候,須要執行的那個Message的時間尚未到,因此咱們在那裏設置了須要等待的時間,也就是對nextPollTimeoutMillis進行了賦值,可是後面執行IdleHandler時須要時間的,若是這裏不設置nextPollTimeoutMillis = 0,那麼下次循環的時候就會調用nativePollOnce(ptr, nextPollTimeoutMillis);方法設置阻塞的時間,這樣的話,一旦在咱們執行IdleHandler的時候有新的須要當即執行的Message插入進來而沒有重置nextPollTimeoutMillis = 0,那麼下次循環的時候仍是按照以前設置的時間阻塞,那麼新加進來的Message就無法當即執行。)。

    返回Looper中的loop()方法

     前面已經瞭解了loop()方法的大致流程,下面再進入到其中的循環中查看代碼:

    1. 首先經過MessageQueuenext()方法獲取須要執行的Message,若是獲取到的Message爲空,則結束循環:
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        
        ···其它代碼···
    複製代碼

     經過上面的MessageQueue中的代碼,咱們已經瞭解了,在MessageQueue中的next()方法中,只有MessageQueue已經退出纔會返回null,可是咱們以前也瞭解過,主線程中建立的MessageQueue是不容許退出的,也就是說正常狀況下,主線程中的MessageQueuenext()方法是不會返回null的,從而說明Looper中的loop()方法中的for()循環在正常狀況下不會退出。

    1. 接下來的代碼基本在作一些打印日誌的操做,比較重要的則是在try...catch中執行的操做,在這裏會將Message分發給具體的Handler去進行處理,代碼以下:
    try {
        msg.target.dispatchMessage(msg);
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);                }
        }
    複製代碼
    1. 執行完成以後則會回收當前的Message
    msg.recycleUnchecked();
    複製代碼
    相關文章
    相關標籤/搜索