標籤:android 非同步訊息 源碼解析
關於非同步訊息的用法,可以看之前的一篇文章http://blog.csdn.net/leelit/article/details/45196827,現在來解析一下源碼。
經典用法
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(); } }
如此一來,我們便可以在其他線程,通過handler來發送訊息給當前線程,達到非同步效果。
流程
1、Looper.prepare();
這個方法輾轉調用了下列的方法:
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對象set了一個Looper對象,由拋出的異常可以看出每個線程只能有一個Looper對象,那麼這個Looper又是用來幹嘛的呢?
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
Looper綁定了當前線程和MessageQueue。
2、mHandler = new Handler() {...};
執行個體化了Handler對象,後面再回來分析這個Handler對象的作用
3、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) { // 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(); } }
第2、6行可以看到,這個方法擷取了Looper對象和主要的MessageQueue對象,重點看13、14行,方法內部是一個死迴圈,不斷從MessageQueue裡面拉取Message,並且是阻塞式的,也就是說拉不到Message就停在那裡了。假設拉到了Message,則調用第27行
msg.target.dispatchMessage(msg);
很明顯,就是將訊息分發給處理者。而這裡的msg.target正是Handler對象。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
我們來分別看下這三個可能,也都是常用情景:
①handler對象post一個任務
handler.post(new Runnable() { @Override public void run() { } });
繼續調用
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
可以看到handler post進去的任務會賦給message後才進行MessageQueue入隊,最終Handler處理這個Message時就會調用這個任務。
②執行個體化Handler對象時傳進一個Callback對象
Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } });
if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } }
最終就會回調這個handleMessage方法。
③最常見的情景,執行個體化時重寫handleMessage(msg)方法,這也是上面訊息分發的最後一種可能。
最後兩個小問題,
1、Message的MessageQueue入隊操作是由Handler對象進行,入隊時就會把target設為當前操作的Handler,這樣處理訊息時就能找到合適的Handler了!
2、主線程的Looper是由系統產生的,找了半天源碼沒找見,但是官方文檔說的很清楚The main looper for your application is created by the Android environment
小結
Handler對象負責將Message傳入MessageQueue,而MessageQueue由Looper對象產生;
Looper對象負責建立MessageQueue對象,並且不斷從MessageQueue取出Message,一旦取出則分發訊息,最終都會直接或間接被Handler處理。
研究AsyncTask源碼可以發現,內部是對Handler的封裝;
實現子線程發送訊息給主線程很常見,試下向子線程發送訊息,會更加明了。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Android:非同步訊息源碼解析