Android消息機制不徹底解析(上)


    Handler和Message是Android開發者經常使用的兩個API,我一直對於它的內部實現比較好奇,因此用空閒的時間,閱讀了一下他們的源碼。

   相關的Java Class:
  • android.os.Message
  • android.os.MessageQueue
  • android.os.Looper
  • android.os.Handler
    相關的C++ Class:
  • android.NativeMessageQueue
  • android.Looper
  • android.LooperCallback
  • android.SimpleLooperCallback
  • android.Message
  • android.MessageHandler

首先,來看看這些類之間的關係:java

 


首先,讓咱們從相對簡單的java實現開始看起:android

 

Message

    Message類能夠說是最簡單的,主要提供了一些成員,用以保存消息數據。
    public int what;//用以表示消息類別

    public int arg1;//消息數據

    public int arg2;//消息數據

    public Object obj;//消息數據
    
    /*package*/ long when;//消息應該被處理的時間
    
    /*package*/ Bundle data;//消息數據
    
    /*package*/ Handler target;//處理這個消息的handler
    
    /*package*/ Runnable callback;//回調函數
    
    // sometimes we store linked lists of these things
    /*package*/ Message next;//造成鏈表,保存Message實例

    值得一提的是,Android提供了一個簡單,可是有用的消息池,對於Message這種使用頻繁的類型,能夠有效的減小內存申請和釋放的次數,提升性能。
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;

    /**
     * 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;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

    /**
     * Return a Message instance to the global pool.  You MUST NOT touch
     * the Message after calling this function -- it has effectively been
     * freed.
     */
    public void recycle() {
        clearForRecycle();

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {//消息池大小未滿,則放入消息池
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
    /*package*/ void clearForRecycle() {
        flags = 0;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        when = 0;
        target = null;
        callback = null;
        data = null;
    }

    小結:
  1. Message的核心在於它的數據域,Handler根據這些內容來識別和處理消息
  2. 應該使用Message.obtain(或者Handler.obtainMessage)函數獲取message實例

 

Handler

    首先看看構造函數:app

 

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

 

    public Handler() {
        this(null, 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 that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback; //使用Callback能夠攔截Handler處理消息,以後會在dispatchMessage函數中,大展身手
        mAsynchronous = async;//設置handler的消息爲異步消息,暫時先無視這個變量
    }

    Handler的構造函數最主要的就是初始化成員變量:mLooper和mQueue。 這邊須要注意的一個問題是:Looper.myLooper()不能返回null,不然拋出RuntimeExeception。稍後詳解Looper.myLooper();函數在何種狀況下會拋出異常。less


    Handler.obtainMessage系列的函數都會調用Message類中對應的靜態方法,從消息池中獲取一個可用的消息實例。典型實現以下:異步

    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }

 


    Handler.post系列和send系列函數最終都會調用enqueueMessage函數,把message入列,不一樣之處在於post系列函數會以Runable參數構建一個Message實例。async

 

     private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;//一會咱們會看到callback非空的message和callback爲空的mesage在處理時的差別
        return m;
    }

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

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

    //最終都會調用這個函數,把message入列
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);//Handler的mAsynchronous屬性,決定了msg是否爲asynchronous,稍後在MessageQueue.next函數中,能夠看到asynchronous對於消息處理的影響        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

    除了這些以外,Handler還提供了hasMessage系列和removeMessages系列函數用以管理Handler對應的MessageQueue中的消息。ide

 


    接下來主角登場,Handler.dispatchMessage:函數

 

    private static void handleCallback(Message message) {
        message.callback.run();
    }
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
    
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//message的callback不爲null,則執行
            handleCallback(msg);
        } else {
            if (mCallback != null) {//若是Hanlder的mCallback成員不爲null,則調用
                if (mCallback.handleMessage(msg)) {//若是handleMessage返回值爲true,則攔截消息
                    return;
                }
            }
            handleMessage(msg);//處理消息
        }
    }

    註釋應該比較清楚,很少說。 小結:oop

  1.  Handler類最爲核心的函數是enqueueMessage和dispatcherMessage,前者把待處理的消息放入MessageQueue,而Looper調用後者來處理從MessageQueue獲取的消息。
  2.  callback不爲null(經過post系列函數添加到消息隊列中)的message沒法被攔截,而callback爲null的函數能夠被Handler的mCallback攔截

 


Looper

    一樣從構造函數看起:
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//每一個Looper有一個MessageQueue
        mRun = true;
        mThread = Thread.currentThread();
    }
     ** 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);//後臺線程的looper都容許退出
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");//每一個線程只能有一個Looper
        }
        sThreadLocal.set(new Looper(quitAllowed));//把實例保存到TLS(Thread Local Save),僅有每一個線程訪問本身的Looper
    }

    /**
     * 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);//主線程的lopper不能夠退出
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    由於是私有的構造函數,因此理論上來講只能經過prepare和prepareMainLooper兩個函數來實例化Looper,可是google的註釋也說的很清楚:prepareMainLooper()應該由系統調用(有興趣的同窗能夠去看看AtivityThread類的main函數),因此,應用開發者可使用的只剩下prepare函數。
    好了,Looper的實例是構造出來,可是如何獲取構造出來的實例呢?
    /** Returns the application's main looper, which lives in the main thread of the application.
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }
    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
     如今,咱們應該知道如何防止Handler實例化的時候,拋出RuntimeException:在守護線程中實例化Handler以前,須要先調用Looper.perpare函數來構造Looper實例。

     而後,重頭戲來了: 
    /**
     * Quits the looper.
     *
     * Causes the {@link #loop} method to terminate as soon as possible.
     */
    public void quit() {
        mQueue.quit();
    }

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {//調用looper以前,須要先調用perpare,不然您懂的...
            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();

        for (;;) {
            Message msg = queue.next(); // might block 獲取一個下一個消息,若是當前沒有要處理的消息,則block,以後咱們會看到這個API的實現
            if (msg == null) {//調用了MessgeQueu的quit函數後,MessageQueue.next會返回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
            Printer logging = me.mLogging;
            if (logging != null) {//藉助logging咱們能夠打印Looper中處理的消息
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);//調用handler處理消息

            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.recycle();//回收消息到消息池
        }
    }
    Looper.loop()函數是Looper類的核心函數,主要循環進行兩個操做:
  1. 從MessageQueue中獲取一個消息,當前沒有消息須要處理時,則block
  2. 調用message的Handler(target)處理消息
    基本上,咱們能夠把Looper理解爲一個死循環,Looper開始work之後,線程就進入了以消息爲驅動的工做模型。

    小結:
  1. 每一個線程最多能夠有一個Looper。
  2. 每一個Looper有且僅有一個MessageQueue
  3. 每一個Handler關聯一個MessageQueue,由該MessageQueue關聯的Looper執行(調用Hanlder.dispatchMessage)
  4. 每一個MessageQueue能夠關聯任意多個Handler
  5. Looper API的調用順序:Looper.prepare >> Looper.loop >> Looper.quit
  6. Looper的核心函數是Looper.loop,通常loop不會返回,直到線程退出,因此須要線程完成某個work時,請發送消息給Message(或者說Handler)

MessageQueue

    
    MessageQueue類是惟一包含native函數的類,咱們先大體看一下,稍後C++的部分在詳細解釋:
    private native void nativeInit();    //初始化
    private native void nativeDestroy(); //銷燬
    private native void nativePollOnce(int ptr, int timeoutMillis); //等待timeoutMillis指定的時間
    private native void nativeWake(int ptr);//喚醒nativePollOnce的等待

    而後,咱們再從構造函數看起:
    
    Message mMessages;//數據域mMessages的類型雖然是Message,可是由於Message.next數據域的緣由,其實mMessage是鏈表的第一個元素

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        nativeInit();//初始化nativeMessageQueue
    }
    對應的,在銷燬的時候:
    @Override
    protected void finalize() throws Throwable {
        try {
            nativeDestroy();//銷燬nativeMessageQueue
        } finally {
            super.finalize();
        }
    }
    
    此外,MessageQueue提供了一組函數(e.g. hasMessage, removeMessage)來查詢和移除待處理的消息,咱們在前面的Handler類上看到的對應函數的實現就是調用這組函數。

    接下來,看看enqueueMessage函數,Handler函數就是調用這個函數把message放到MessageQueue中:
    final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {//檢查msg是否在使用中,一會咱們能夠看到MessageQueue.next()在返回前經過Message.makeInUse函數設置msg爲使用狀態,而咱們以前看到過Looper.loop中經過調用調用Message.recycle(),把Message重置爲未使用的狀態。
            throw new AndroidRuntimeException(msg + " This message is already in use.");
        }
        if (msg.target == null) {//msg必須知道由那個Handler負責處理它
            throw new AndroidRuntimeException("Message must have a target.");
        }

        boolean needWake;
        synchronized (this) {
            if (mQuiting) {//若是已經調用MessageQueue.quit,那麼再也不接收新的Message
                RuntimeException e = new RuntimeException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            }

            msg.when = when;
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {//插到列表頭
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;//當前MessageQueue處於block狀態,因此須要喚醒
            } 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.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();//當且僅當MessageQueue由於Sync Barrier而block,而且msg爲異步消息時,喚醒。 關於msg.isAsyncChronous(),請回去看看Handler.enqueueMessage函數和構造函數
                Message prev;
                for (;;) {// 根據when的大小順序,插入到合適的位置
                    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;
            }
        }
        if (needWake) {
            nativeWake(mPtr);//喚醒nativeMessageQueue
        }
        return true;
    }
 
    final void quit() {
        if (!mQuitAllowed) {//UI線程的Looper消息隊列不可退出
            throw new RuntimeException("Main thread not allowed to quit.");
        }


        synchronized (this) {
            if (mQuiting) {
                return;
            }
            mQuiting = true;
        }
        nativeWake(mPtr);//喚醒nativeMessageQueue
    }
    關於sync barrier,再補充點解釋: sync barrier是起到了一個阻塞器的做用,它能夠阻塞when>它(即執行時間比它晚)的同步消息的執行,但不影響異步消息。sync barrier的特徵是targe爲null,因此它只能被remove,沒法被執行。MessageQueue提供了下面兩個函數來控制MessageQueue中的sync barrier(如何以爲sync barrier和異步消息難以理解的話,選擇性無視就好,由於它們不妨礙咱們理解Android消息機制的原理):
    final int enqueueSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

    final void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        final boolean needWake;
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;//其實我以爲這邊應該是needWake = mMessages != null && mMessages.target != null
            }
            p.recycle();
        }
        if (needWake) {
            nativeWake(mPtr);//有須要的話,喚醒nativeMessageQueue
        }
    }

    重頭戲又來了:
  final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();//不太理解,選擇性無視
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);//等待nativeMessageQueue返回,最多等待nextPollTimeoutMillis毫秒

            synchronized (this) {
                if (mQuiting) {//若是要退出,則返回null
                    return null;
                }

                // 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) {//下一個消息爲sync barrier
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());//由於存在sync barrier,僅有異步消息能夠執行,因此尋在最近的異步消息
                }
                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);//消息還沒到執行的時間,因此咱們繼續等待msg.when - now毫秒
                    } else {
                        // Got a message.
                        mBlocked = false;//開始處理消息了,因此再也不是blocked狀態
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;//從鏈表中間移除message
                        } else {
                            mMessages = msg.next;//從鏈表頭移除message
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();//標記msg正在使用
                        return msg;//返回到Looper.loop函數
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;//沒有消息能夠處理,因此無限制的等待
                }

                // 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)) {// 目前無消息能夠處理,能夠執行IdleHandler
                    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("MessageQueue", "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;//Looper.looper調用一次MessageQueue.next(),只容許調用一輪IdleHandler

            // 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;//由於執行IdleHandler的過程當中,可能有新的消息到來,因此把等待時間設置爲0
        }
    }
              爲了方便你們理解Message的工做原理,先簡單描述nativeWake,和natePollonce的做用:
  1. nativePollOnce(mPtr, nextPollTimeoutMillis);暫時無視mPtr參數,阻塞等待nextPollTimeoutMillis毫秒的時間返回,與Object.wait(long timeout)類似
  2. nativeWake(mPtr);暫時無視mPtr參數,喚醒等待的nativePollOnce函數返回的線程,從這個角度解釋nativePollOnce函數應該是最多等待nextPollTimeoutMillis毫秒

    小結:
  1. MessageQueue做爲一個容器,保存了全部待執行的消息。
  2. MessageQueue中的Message包含三種類型:普通的同步消息,Sync barrier(target = null),異步消息(isAsynchronous() = true)。
  3. MessageQueue的核心函數爲enqueueMessage和next,前者用於向容器內添加Message,而Looper經過後者從MessageQueue中獲取消息,並實現無消息狀況下的等待。
  4. MessageQueue把Android消息機制的Java實現和C++實現聯繫起來。
    原本我是想一口氣把java實現和C++實現都寫完的,可是,無奈最近工做和我的事務都比較多,稍後爲你們奉上C++實現的解析。
相關文章
相關標籤/搜索