Android中Handler的理解與總結

android的異步處理消息機制Handler這個問題是老生常談哪,這個要追溯到一個面試的場景了,面試官說,handler發送完消息後,什麼時候觸發循環,這個我說了,handler源碼中有個looper,這個是用來循環取出handler發送到消息隊列(messageQueue)中的消息,一旦Looper開啓Looper.loop()就開啓無限循環,直接取出MessageQueue中所有的消息,然後面試官不知道是他怎麼理解的,我說的是looper.loop()就開啓循環,他沒說話,可能不在一個頻道上抑或是我回答的不對?我感覺他也不是特別的理解吧,哎,有點坑哪,這面試真是醉了,什麼樣的面試官都會遇到,面試還真是運氣加上實力,我覺得吧,可能還是緣分未到吧,但是我喜歡總結,我覺得自己要深刻的理解,現在我就去再去深入的看下這個Handler,下次再問到此類問題信手拈來,插一句,可能那個面試官他真的不是特別理解吧!

 首先,咱們理解幾個概念,見名知意吧,下面的博客中會以口語化形式表述出來。

 Message(消息),MessageQueue(消息隊列),Looper(循環,很重要),Handler(用來發送和處理消息)

1.Handler分析發送消息的過程

Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};
上面那種寫法咱們是咱們最常用的,new完後,重寫handleMessage方法,裏面的傳遞介質Message就是我們發送的消息,我們看看他的內部是如何處理的,直接點擊Handler

/**
 * Default constructor associates this handler with the {@link Looper} for the
 * current thread.
 *
 * If this thread does not have a looper, this handler won't be able to receive messages
 * so an exception is thrown.
 */
public Handler() {
    this(null, false);
}
看到他的構造函數使用的這類裏面的構造,傳入參數是null 和 false,我們繼續點擊this這個函數

/**
 * Use the {@link Looper} for the current thread with the specified callback interface
 * and set whether the handler should be asynchronous.
 *
 * Handlers are synchronous by default unless this constructor is used to make
 * one that is strictly asynchronous.
 *
 * Asynchronous messages represent interrupts or events that do not require global ordering
 * with respect to synchronous messages.  Asynchronous messages are not subject to
 * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
 *
 * @param callback The callback interface in which to handle messages, or null.
 * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
 * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
 *
 * @hide
 */
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;
    mAsynchronous = async;
}
第一個參數是一個回調的接口,這個接口也是在Handler內部定義的如下所示:

/**
 * 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);
}
其實也是最後調用handlerMessage(Message msg)這個方法,就像我們剛纔使用的那種方式,我們可以這樣理解,這個Handler有好幾種的使用方式,可以傳接口,也可以直接new出來,然後重寫handlerMessage方法,幾個入口吧。

第二個參數是一個boolean類型,這個消息是否是異步的,這裏我們new出來的默認是false,也就是不是異步的消息,是同步的消息,異步的消息這裏提前說下,異步消息就不能保證順序了,因爲這裏面還有MessageQueue消息隊列的概念,下面會繼續說的。

ok,new完handler後,可以看到給mLooper 設置了與當前線程相關聯的Looper對象,mQueue爲當前looper對象裏面的消息隊列,而looper和messageQueue是在Looper對象實例化後相關聯的,可以看下兩者的關聯代碼

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
ok,上面講的是new完Handler後的代碼追蹤,也就是在newHanlder後,獲取到當前的looper對象並設置到Handler裏面的成員變量mLooper,還有將Handler成員變量消息隊列mQueue 設置爲當前Looper對象所關聯的消息隊列mLooper.mQueue;

下面這個步驟就是handler發送消息了,追蹤下代碼

/**
 * Enqueue a message into the message queue after all pending messages
 * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
 * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
 * Time spent in deep sleep will add an additional delay to execution.
 * You will receive it in {@link #handleMessage}, in the thread attached
 * to this handler.
 * 
 * @param uptimeMillis The absolute time at which the message should be
 *         delivered, using the
 *         {@link android.os.SystemClock#uptimeMillis} time-base.
 *         
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the message will be processed -- if
 *         the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
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);
}
不管你是sendMessage還是sendMessageDelayed最終代碼都會執行到這個sendMessageAtTime方法中,可以看到第一個參數是Message我們的傳遞介質,消息載體,第二個就是時間,消息的絕對時間,然後接着追蹤代碼

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
在這裏可以看到msg.target = this,可以很好的理解,即當前消息的目標,也就是發送消息的句柄是當前的對象也就是Handler,將其賦值,然後判斷是否是異步的消息,如果是,設置爲true,然後將這個消息入隊返回true 或者false,表示消息進入隊列是否成功

繼續往下看

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    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;
        Message p = mMessages;
這是消息入隊的代碼,會先判斷當前目標target是否爲null,這個消息是否正在使用等最後返回true,表示消息入隊成功。

 2.疑問:到這裏我們可能要問了,這邏輯已經順着下來了,handler什麼時候去處理消息哪,現在只有入沒有處理消息啊?

   ok,上述1的過程只是消息的發送過程,現在我們來看看消息的處理,我們不要忘記了Looper這個對象,先查看其源碼,其中有這個方法

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {

當looper調用這個方法後會開啓無限循環,循環從messageQueue中取出message,然後調用msg.target.dispatchMessage去處理消息,可以看下代碼

try {
    msg.target.dispatchMessage(msg);
} finally {
    if (traceTag != 0) {
        Trace.traceEnd(traceTag);
    }
}
而target就是當前new的handler,我們看handler裏面dispatchMessage的方法是如何處理的

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
看到上面的註釋說明”在這裏處理系統的消息“,然後我們驚喜的發現了callback,對,沒錯,就是在最初初始化handler的時候,傳的callback參數,但是我們這個是null啊,對的,所有他執行了else,執行了handleMessage,然後這就是我們爲什麼重寫handleMessage的原因,好了,但是callback的使用場景是什麼

new Handler().post(new Runnable() {
    @Override
    public void run() {
       new TextView(TestActivity.this).setText("xxxxx");
    }
});
我們直接使用handler.post 發送了一個runnable,點擊post後,可以看到執行的順序和sendMessage是一樣的

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
不過是通過getPostMessage(r)獲取了一個消息,而此時callback = r;
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

此時的callback不爲空,那最後執行到這個dispatchMessage時,由於callback不是null,所以直接執行了runnable的run方法,不信,請看代碼

private static void handleCallback(Message message) {
    message.callback.run();
}
整個流程走完了,但是回到最初的問題,面試官說,什麼時候觸發循環,因爲我們已經知道必須調用looper.loop()方法才能觸發無限循環,說這樣也沒錯啊,但是我們new的時候並沒有使用looper的loop方法啊,ok,問題就在這裏,我們Activity在初始化的時候,系統已經幫我們做好了

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
    sMainThreadHandler = thread.getHandler();
}

if (false) {
    Looper.myLooper().setMessageLogging(new
            LogPrinter(Log.DEBUG, "ActivityThread"));
}

// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
在ActivityThread這個類中,系統初始了mainLooper,所以new Handler後默認爲mainLooper,最下面的那個Looper.loop(),系統已經幫助我們開啓消息循環了,比如我們之前經常這樣寫

  new Thread(){
        public void run(){
            Looper.prepare();
            handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
            Looper.loop();
        }
    }.start();
}
上述代碼是在子線程中使用handler,這個時候要注意,因爲已經切換了線程,不再是默認的UI主線程,所以looper也不再是main Looper,所以Looper.prepare是將當前子線程綁定到當前looper對象,最後一定要開啓消息循環,這是最經典的寫法和使用。

綜上所述,可能我沒有get到面試官的點吧,抑或是面試官在這個問題上也存在着疑問吧,不管怎麼說吧,自己都要要求自己去理解,不能再知道表層了,與君共勉吧!


參考文章:

http://blog.csdn.net/lmj623565791/article/details/38377229


  

發佈了60 篇原創文章 · 獲贊 110 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章