android消息機制 - Handler、Looper原理解析

android的消息機制就是指Handler、MessageQueue和Looper的工作過程。

需要傳遞消息時,由Handler會調用MessageQueue的enqueueMessage方法將消息放入消息隊列中,Looper會不斷從隊列裏取消息,取出以後再交給Handler去處理。

Handler在創建時要使用當前線程的Looper來構建內部的消息處理系統,所以如果當前線程沒有Looper,就會報錯:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

我們在主線程(ActivityThread)裏使用Handler時通常都是直接使用,並沒有創建Looper,那是因爲系統會通過Looper.prepareMainLooper()來創建主線程的Looper,所以主線程中默認可以直接使用Handler。


Looper的工作原理

Looper的構造方法裏會新建MessageQueue並保存當前的線程信息,所以MessageQueue是由Looper去創建並維護的。

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

如何去創建一個Looper?

Looper.prepare();

上述代碼只是在當前線程裏創建了Looper對象,要想使其工作(可以不斷的從消息隊列中取消息並處理消息)還需要調用:

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

    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方法是一個死循環,不斷的調用queue.next()去從消息隊列中取消息。當消息爲null時纔會返回。需要注意的是,如果當前消息隊列裏沒有消息,queue.next()方法會阻塞住而並不是返回null。那麼什麼時候纔是null退出循環呢?只有調用Looper.quit()或Looper.quitSafely()方法,這樣就會調用MessageQueue的quit/quitSafely來通知消息隊列退出,這時queue.next()就會返回null了。

quit和quitSafely方法的區別是:quit會直接退出Looper,而quitSafely會等待當前消息隊列中已有的消息全部處理完畢後才完全退出。

在子線程中,如果手動創建了Looper,那麼當所有事情完成以後應該調用quit方法來終止消息循環,否則子線程會一直處於等待的狀態。而當Looper退出後,Handler的send方法會返回false,線程會立刻終止,因此建議不需要的時候終止Looper。

通過queue.next()取得消息後,Looper會調用msg.target.dispatchMessage(msg); 這裏的msg.target就是指Handler,這樣發送的消息又交給它的dispatchMessage方法來處理了。


Handler的工作原理

我們經常使用Handler的sendMessage或sendMessageDelayed的方法,它們最終都會調到enqueueMessage()來往消息隊列中插入消息。然後消息交給Looper處理,Looper又調用Handler的dispatchMessage方法。dispatchMessage的源碼如下所示:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

通過源碼可知,會首先檢查Message的callback是否爲null。Message的callback實際上就是Handler的post方法所傳遞的Runnable參數,這裏調用的handleCallback(msg)就是要去執行Runnable的run方法:

private static void handleCallback(Message message) {
    message.callback.run();
}



其次會檢查mCallback是否爲null,這個Callback是指:

 /**
  * Callback interface you can use when instantiating a Handler to avoid
  * having to implement your own subclass of Handler.
  *
  * @param msg A {@link android.os.Message Message} object
  * @return True if no further handling is desired
  */
 public interface Callback {
     public boolean handleMessage(Message msg);
 }

當我們創建Handler對象時,如果是用這個構造方法,就相當於爲mCallback賦值了:

public Handler(Callback callback) {
    this(callback, false);
}

我們在日常使用Handler時,經常繼承它並實現handleMessage方法,實際並沒有必要,只要傳入Callback就可以。


Handler還有直接傳入Looper的構造方法:

public Handler(Looper looper) {
    this(looper, null, false);
}

總結

通過上述原理分析,可以總結出在子線程中使用Handler的方式概括如下:

new Thread(){
    @Override
    public void run() {
        Looper.prepare();
        Handler handler = new Handler();
        Looper.loop();
    }
}.start();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章