上一篇博文給出了Android中基于Handler Looper機制實現(xiàn)線程間通信的兩個典型實例。本文將對該機制的基本原理進行較深入的研究。個人認為,學(xué)好Android編程最好的老師就是Android的源代碼,下面將基于Android-19的源碼進行分析,重點闡述分析思路。
成都創(chuàng)新互聯(lián)公司主要從事成都做網(wǎng)站、網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)威寧,十年網(wǎng)站建設(shè)經(jīng)驗,價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792
要分析Handler Looper機制,自然想到去看Handler類和Looper類的源碼(分別位于Handler.java和Looper.java兩個文件中)。簡單閱讀兩個類的描述后,在Looper類的描述中能找到以下一段示例代碼。
*This is a typical example of the implementation of a Looper thread, * using the separation of {@link #prepare} and {@link #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(); * } * }*/
這段代碼給出了Handler Looper機制實現(xiàn)進程間通信的三大基本步驟,包括Looper的兩個函數(shù)prepare()和loop(),以及Handler的handleMessage函數(shù)。上一篇博文中實例二模擬子線程向UI主線程傳遞信息的程序就基本上是直接copy這段示例代碼實現(xiàn)的。
先看第一個步驟:調(diào)用Looper.prepare()函數(shù),猜測應(yīng)該是創(chuàng)建Looper對象,做些初始化工作。代碼如下:
/** 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); }
直接調(diào)用了重載函數(shù)prepare(true);
//ThreadLocal實例為多個線程共享,但保證每個線程的存儲空間相互獨立 static final ThreadLocalsThreadLocal = new ThreadLocal (); //消息隊列 final MessageQueue mQueue; //當(dāng)前線程引用 final Thread mThread; private static void prepare(boolean quitAllowed) { //保證一個線程最多只能創(chuàng)建一個Looper對象。 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //創(chuàng)建Looper對象并存儲到當(dāng)前線程獨立的存儲空間中 sThreadLocal.set(new Looper(quitAllowed)); } private Looper(boolean quitAllowed) { //創(chuàng)建消息隊列(線程間即通過該消息隊列實現(xiàn)通信) mQueue = new MessageQueue(quitAllowed); //獲得當(dāng)前線程的引用 mThread = Thread.currentThread(); }
看到這里明白了,Looper的prepare函數(shù)實際上創(chuàng)建了Looper對象并把對象保存到當(dāng)前線程獨立的存儲空間中。這里,Looper的構(gòu)造函數(shù)是私有的,所以外部無法直接通過new Looper()隨意創(chuàng)建Looper對象。而只能通過Looper的prepare()函數(shù)創(chuàng)建。這樣做能夠保證對于某一個線程,最多只會創(chuàng)建一個Looper對象的實例,這實際上就是設(shè)計模擬中的單體模式。此外,在Looper的私有構(gòu)造函數(shù)中還創(chuàng)建了消息隊列并獲得當(dāng)前線程(即創(chuàng)建Looper的線程)的引用。
先跳過Handler.handleMessage(Message msg),直接看Looper.loop()的實現(xiàn)。
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() {ui //myLooper()函數(shù)通過sThreadLocal.get()判斷當(dāng)前線程是否已經(jīng)創(chuàng)建了Looper的實例 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(); //這里開始無限循環(huán) 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); } //這里是關(guān)鍵,調(diào)用了dispatchMessage函數(shù)對從消息隊列取出的msg進行分派 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.recycle(); } }
到這里,Looper扮演的角色已經(jīng)明朗了,主要就是通過loop()函數(shù)中的那個無限循環(huán)不斷從消息隊列中取出消息,并通過dispatchMessage()方法將消息派送出去。那么消息隊列中的消息從哪里來,又會被派送到哪里呢?
先來分析第一個問題,消息從哪里來。上一篇博文的實例中,消息源線程均通過調(diào)用Handler的sendMessage()函數(shù)來發(fā)送消息。進入Handler.java文件看其中的sendMessage()函數(shù)。
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
這里調(diào)用了sendMessageDelayed,延時0毫秒。
public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
進一步調(diào)用了sendMessageAtTime,在當(dāng)前時刻發(fā)出。
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); }
再看enqueueMessage函數(shù)。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //設(shè)置target handler msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //將消息插入到消息隊列中 return queue.enqueueMessage(msg, uptimeMillis); }
看到這里已經(jīng)明朗了,消息源線程通過Handler.sendMessage發(fā)送消息,實際上就是把消息插入了與之關(guān)聯(lián)的消息隊列中。在enqueueMessage函數(shù)中有一條關(guān)鍵語句msg.target = this,通過這條語句就把Handler和Looper關(guān)聯(lián)起來了(在Looper.loop()的循環(huán)中就是通過msg.target屬性找到發(fā)送消息的Handler并調(diào)用其dispatchMessage()函數(shù)派發(fā)消息的).
搞清楚了消息從哪里來,接下來分析消息被派發(fā)到哪里,接著看dispatchMessage()函數(shù)的實現(xiàn)。
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { //若消息對象實現(xiàn)了其中的Runnable接口,調(diào)用對應(yīng)的回調(diào)函數(shù),即為message.callback.run()) if (msg.callback != null) { handleCallback(msg); } else { //若實現(xiàn)了Handler類的Callback接口,調(diào)用接口的回調(diào)函數(shù) if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //將消息返回Handler的消息處理回調(diào)函數(shù)(是Handler的成員函數(shù),示例代碼中回調(diào)的就是該函數(shù)) handleMessage(msg); } }
可見,dispatchMessage函數(shù)中根據(jù)msg,handler對象的設(shè)置情況調(diào)用相應(yīng)的消息處理回調(diào)函數(shù),我們只需要在這個回調(diào)函數(shù)中添加代碼,就可以進行消息處理。示例代碼的第二個步驟中的handleMessage函數(shù)就是在這里被回調(diào)的。
下面回到示例代碼中的第二個步驟:
* mHandler = new Handler() { * public void handleMessage(Message msg) { * // process incoming messages here * } * };
這段代碼創(chuàng)建了Handler的對象,并覆蓋了其中的HandleMessage方法,用戶可以添加自己的消息處理函數(shù)。 handleMessage回調(diào)函數(shù)上面已經(jīng)分析過了,下面主要看看創(chuàng)建handler對象時都做了哪些事情。轉(zhuǎn)入Handler.java文件,看Handler的構(gòu)造函數(shù)(找不帶參數(shù)那個)。
/** * 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); }
調(diào)用了下面的雙參數(shù)的構(gòu)造函數(shù)。
final Looper mLooper; final MessageQueue mQueue; 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()); } } //獲得當(dāng)前線程Looper對象的引用 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //獲得Looper關(guān)聯(lián)的消息隊列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
到這里發(fā)現(xiàn)Handler的構(gòu)造函數(shù)主要做了兩件事:1)獲得當(dāng)前線程Looper對象的應(yīng)用.2)獲得與Looper關(guān)聯(lián)的消息隊列的引用。到這里,Handler、Looper、消息隊列三者就已經(jīng)關(guān)聯(lián)起來了。
下面通過一個示意圖對上面的分析進行總結(jié)。
由圖可見,基于Handler Looper機制傳遞消息主要包括以下幾個步驟。
(1)目標(biāo)線程調(diào)用Looper.prepare()創(chuàng)建Looper對象和消息隊列。
(2)目標(biāo)線程通過new Handler()創(chuàng)建handler對象,將Handler,Looper,消息隊列三者關(guān)聯(lián)起來。并覆蓋其handleMessage函數(shù)。
(3)目標(biāo)線程調(diào)用Looper.loop()監(jiān)聽消息隊列。
(4)消息源線程調(diào)用Handler.sendMessage發(fā)送消息。
(5)消息源線程調(diào)用MessageQueue.enqueueMessage將待發(fā)消息插入消息隊列。
(6)目標(biāo)線程的loop()檢測到消息隊列有消息插入,將其取出。
(7)目標(biāo)線程將取出的消息通過Handler.dispatchMessage派發(fā)給Handler.handleMessage進行消息處理。
到這里整個Android的Handler Looper機制傳遞消息原理就分析完畢了。還有一個問題值得一提,回顧一下上一篇博文的示例1模擬從網(wǎng)絡(luò)上下載數(shù)據(jù)的程序,UI主線程只通過new Handler()創(chuàng)建了Handler對象的實例并覆蓋了其handleMessage函數(shù)。在代碼中并沒有看到調(diào)用Looper.prepare和Looper.loop(),那么UI主線程中沒有創(chuàng)建Looper對象嗎?下面就來分析這個問題,既然是UI主線程,那么自然是在啟動應(yīng)用時候由系統(tǒng)自動創(chuàng)建的,創(chuàng)建過程中是否已經(jīng)創(chuàng)建了Looper對象并調(diào)用loop()進行監(jiān)聽了呢?轉(zhuǎn)到ActivityThread.java,找到其中的main函數(shù),這里即為Android程序的入口。在其中能看到以下兩行代碼。
Looper.prepareMainLooper(); ...... ...... Looper.loop();
再看prepareMainLooper函數(shù)的實現(xiàn):
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
在這里調(diào)用了prepare創(chuàng)建Looper對象。所以說,對于UI主線程而言,其Looper對象是由系統(tǒng)創(chuàng)建好的,用戶就無需自行創(chuàng)建了。