從源碼分析Handler機制,源碼handler機制
在Android中,當要更新ui的時候,我們必須要在主線程中進行更新,原因時當主線程被阻塞了5s以上就會出現anr異常,會導致程式崩潰。所以一些耗時的操作必須要放在子線程中,但是在子線程中又不能做更新ui的操作,所以為瞭解決這個問題,Android設計了handler機制,handler的出現建立起了主線程與子進程之間的通訊橋樑,使得ui更新問題得到改善,下面就來剖析一下handler。ActivityThread啟動了應用程式的主線程,在ActivityThread的main方法中:
public static final void main(String[] args) { SamplingProfilerIntegration.start(); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); ......}
從上述代碼可以看出,首先要執行Looper.prepareMainLooper();操作,然後進入loop進行迴圈。在prepareMainLooper中,調用prepare方法使用sThreadLocal給當前線程設定一個Looper,如果當前線程中沒有,就初始化一個Looper,在Looper的構造方法中順便建立了一個MessageQueue。細心的讀者可能會注意到prepareMainLooper和prepare方法都是static的,sThreadLocal也是個靜態變數,首先不考慮子線程存在的情況,只考慮主線程,所以無論我們在應用程式的哪個地方調用Looper.prepareMainLooper();通過sThreadLocal.get()得到的都是同一個looper對象,這樣就可以保證一個線程中只有一個Looper對象,那麼也就意味著一個線程中只有一個MessageQueue。
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }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)); }private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
執行完 Looper.prepareMainLooper()之後,就是開始Looper.loop()進行訊息的迴圈讀取並且進行分發,這個稍後分析完Handler後再分析。
下面我們再分析一下Handler。
在代碼中我們經常的這樣用:
private Handler handler = new Handler(){ public void handleMessage(Message msg) { // process incoming messages here }}public Handler(Callback callback, boolean async) { ...... 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的構造方法中,通過Looper.myLooper()擷取本線程中唯一的一個Looper對象,並且初始化hanlder中的訊息佇列,這個訊息佇列和Looper中的一開始初始化的訊息佇列是同一個。
當調用handler.sendMessage或者sendEmpty方法時,最終要走的方法都是sendMessageAtTime方法:
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);}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}
在enqueueMessage方法中,給當前要加入訊息佇列的msg設定一個target為this,這個this也就是當前的handler對象,主要是為了後面的looper迴圈出訊息後,方便知道這個msg向何處分發,該由哪個handler進行處理。接著就調用MessageQueue的enqueueMessage方法將msg排入佇列中。
boolean enqueueMessage(Message msg, long when) { ...... synchronized (this) { ...... msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { 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); } } return true;}
當訊息對列中沒有任何msg的時候,當前加入的msg就應該是隊列的隊頭,並且從else語句我們可以看出,整個訊息對列是個迴圈對列。此時訊息對列中已經有了msg,那麼這個msg應該被接受並進行分發處理,在ActivityThread中調用了Looper.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) { return; } Printer logging = me.mLogging; ....... msg.target.dispatchMessage(msg); final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { } msg.recycle(); }}
其中的for迴圈為死迴圈,有人可能納悶了,looper.loop()是運行在主線程中的,而其中有是個死迴圈,不是說好的主線程中不能做逾時的操作嗎?呵呵,因為在迴圈中輪詢訊息佇列中的訊息時候,如果沒有訊息,則會被阻塞。所以這裡不用擔心anr的問題。通過queue.next()擷取出msg後,通過msg.target.dispatchMessage(msg)處理這個訊息,這個msg.target就是要處理訊息的handle,這也就是為啥在handler中要重寫dispatchMessage方法的原因。最後調用recycle釋放訊息,之所以要recycle一下,是因為Message可以不用new的方式,也可以通過Message.obtain方法從訊息池中擷取一個,因為訊息池中的訊息個數有限,如果用完訊息後,不及時的recycle的 話,就會造成msg對象不能重複利用。
接下來具體的分析下queue.next()這個方法,在注釋中我們看到,這個方法有可能會被阻塞,阻塞的原因是訊息佇列中沒有訊息。
Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } // We can assume mPtr != 0 because the loop is obviously still running. // The looper will not call this method after the loop quits. nativePollOnce(mPtr, 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 (false) Log.v("MessageQueue", "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("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; // 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; }}
nativePollOnce(mPtr, nextPollTimeoutMillis)當輪詢沒有訊息時,會進行阻塞。訊息喚醒和阻塞機制將會在下一篇文章進行介紹,請大家關注。
最後對Handler做一下總結。從訊息的分發一直到訊息的處理,先後接觸到的幾個名詞有Looper、MessageQueue、Thread、Handler、Message。
- Looper:負責初始化訊息佇列,並不斷的從訊息佇列中輪詢訊息。
- MessageQueue : 是訊息佇列,存放在handler發送過來的訊息。
- Thread:當前訊息佇列和looper所操作的場所或者說啟動並執行環境。
- Handler:負責訊息的發送和處理。
- Message:一個更新UI的訊息,由handler發出,由MessageQueue列隊。
訊息處理機制大概的一個處理過程如下:
關於輪詢的時候,阻塞和喚醒機制請看下一篇文章。
補充:在子線程中要更新ui的時候,可以這樣處理
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(); } }
著作權聲明:本文為博主原創文章,未經博主允許不得轉載(連絡方式:QQ312037487 郵箱:andywuchuanlong@sina.cn)。