1. 程式人生 > >android Looper 原始碼解析

android Looper 原始碼解析

android Looper 官網解釋:

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare in the thread that is to run the loop, and then loop to have it process messages until the loop is stopped.

翻譯成中文:

Looper 類,用於執行執行緒的訊息迴圈。執行緒預設情況下沒有與它們相關聯的訊息迴圈;要建立一個執行緒,需要線上程中準備執行迴圈,然後迴圈執行程序訊息,直到迴圈停止。

This is a typical example of the implementation of a Looper thread,using the separation of prepare and loop to create an initial Handler to communicate with the Looper.

class LooperThread extends Thread {
       public Handler mHandler;

       public void run() {
           Looper.prepare();
           mHandler = new
Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }

通過上面的示例程式碼可以看出Looper的主要使用場景 —非主執行緒需要Handler傳送訊息。

在上述示例中提到了Looper中兩個重要的方法 perpare()、loop()

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

prepare()方法中使用了ThreadLocal物件,而該物件是一個執行緒內部的資料儲存類,具體的內部原理可以參考:

同時在該方法中創建出了一個Looper物件:

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

在Looper的構造方法中創建出一個新的MessageQueue物件,該物件的內部原理可以參考:

xxxxxxx

至此可以看出當Looper呼叫prepare()方法時,實質上只是創建出一個MessageQueue佇列。

示例demo中在呼叫prepare()方法之後,緊接著呼叫了 Looper.loop()方法,該方法在Looper.java 中的示例原始碼:

  /**
     * 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) {
            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
            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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(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();
        }
    }

loop方法主要是:線上程中執行訊息佇列,並從訊息佇列中獲取訊息Message:對於Message的內部實現原理可以參考:

xxxxx

下面來具體分析loop方法的具體實現。

final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }

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

在呼叫loop方法時,會首先判斷當前執行緒中是否已存在looper物件,如果當前執行緒中looper物件為null 則丟擲異常:No Looper; Looper.prepare() wasn’t called on this thread.

 final MessageQueue queue = me.mQueue;

之後通過looper物件 獲取MessageQueue物件。

Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

以上兩行程式碼在Binder.java 中可以看出實質是通過jni呼叫了底層處理,具體處理暫時無法獲取,通過註釋

 /**
     * Reset the identity of the incoming IPC on the current thread.  This can
     * be useful if, while handling an incoming call, you will be calling
     * on interfaces of other objects that may be local to your process and
     * need to do permission checks on the calls coming into them (so they
     * will check the permission of your own local process, and not whatever
     * process originally called you).
     *
     * @return Returns an opaque token that can be used to restore the
     * original calling identity by passing it to
     * {@link #restoreCallingIdentity(long)}.
     *
     * @see #getCallingPid()
     * @see #getCallingUid()
     * @see #restoreCallingIdentity(long)
     */
    public static final native long clearCallingIdentity();

可以看出clearCallingIdentity() 方法的目的是:確保此執行緒的標識是本地程序的標識,並跟蹤此執行緒本地標識。

同時在loop方法的for(;;) 迴圈中看到

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

每次迴圈 都會檢查 此執行緒的本地標識是否一致,如果不一致 則會執行Log列印否則 不會執行。

緊接著來細看loop中的for(;;)迴圈具體做了什麼?

  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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(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();
        }

for (;;) 代表一個無休止迴圈。如果迴圈中沒有終止,那麼該迴圈將會一直迴圈下去。
在該迴圈中 只需要關注這幾行程式碼即可,而像另外的幾行程式碼,在if條件成立的情況下是一個logging日誌的列印。

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

上述幾行程式碼可以看到 通過MessageQueue的next()方法獲取到佇列中的Message訊息,如果訊息為null 則跳出迴圈條件
如果不為null 那麼直接呼叫 msg.target = Handler 物件的dispatchMessage(msg) 方法
最後 直接呼叫Message物件的recycleUnchecked()方法,而該方法主要是對訊息進行回收。

至此可以看出loop方法的主要作用是一個無限for迴圈,在該迴圈中如果MessageQueue中訊息不為null,那麼將會從該佇列中一直去取Message,並將該Message通過引數方法傳遞給Handler。

下面在介紹一下Looper.java中其它的一些方法:

 public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }

獲取當前管理的MessageQueue物件

 /**
     * 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物件,並將該looper物件標記為application main 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;
        }
    }

獲取主執行緒的Looper物件

Looper是通過呼叫loop方法驅動著訊息迴圈的進行: 從MessageQueue中阻塞式地取出一個訊息,然後讓Handler處理該訊息,周而復始,loop方法是個死迴圈方法。

那如何終止訊息迴圈呢?我們可以呼叫Looper的quit方法或quitSafely方法,二者稍有不同

 /**
     * Quits the looper.
     * <p>
     * Causes the {@link #loop} method to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @see #quitSafely
     */
 public void quit() {
        mQueue.quit(false);
    }

當我們呼叫Looper的quit方法時,實際上執行了MessageQueue中的removeAllMessagesLocked方法,該方法的作用是把MessageQueue訊息池中所有的訊息全部清空,無論是延遲訊息(延遲訊息是指通過sendMessageDelayed或通過postDelayed等方法傳送的需要延遲執行的訊息)還是非延遲訊息。

 /**
     * Quits the looper safely.
     * <p>
     * Causes the {@link #loop} method to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * However pending delayed messages with due times in the future will not be
     * delivered before the loop terminates.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p>
     */
    public void quitSafely() {
        mQueue.quit(true);
    }

當我們呼叫Looper的quitSafely方法時,實際上執行了MessageQueue中的removeAllFutureMessagesLocked方法,通過名字就可以看出,該方法只會清空MessageQueue訊息池中所有的延遲訊息,並將訊息池中所有的非延遲訊息派發出去讓Handler去處理,quitSafely相比於quit方法安全之處在於清空訊息之前會派發所有的非延遲訊息。

注:無論是呼叫了quit方法還是quitSafely方法只會,Looper就不再接收新的訊息。即在呼叫了Looper的quit或quitSafely方法之後,訊息迴圈就終結了,這時候再通過Handler呼叫sendMessage或post等方法傳送訊息時均返回false,表示訊息沒有成功放入訊息佇列MessageQueue中,因為訊息佇列已經退出了。

需要注意的是Looper的quit方法從API Level 1就存在了,但是Looper的quitSafely方法從API Level 18才新增進來。

至此Looper.java的原始碼解析已經解析完成,如果不明白的地方讀者可留言,或者從android原始碼中自行理解。