前面寫了兩篇源碼解析了,Handler源碼解析和MessageQueue源碼解析,其中MessageQueue源碼解析裡面情況解釋的不是太清晰,隨著以後對代碼的理解,會有後續內容的添加。
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.Most interaction with a message loop is through the Handler class.This is a typical example of the implementation of a Looper thread, using the separation of prepare() and loop() to create an initial Handler to communicate with the Looper.
類用於為一個線程運行一個訊息迴圈。預設情況下線程沒有相關聯的訊息迴圈;可以通過線上程裡調用looper的prepare()方法建立一個looper,然後就會調用loop()處理訊息之道loop被停止。
大多數訊息是通過handler來處理。
下面是一個典型的Looper thread的實現,使用分離的準備()和迴圈()來建立一個Handler與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(); } }
先看一下類中的變數:
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); final MessageQueue mQueue; final Thread mThread; volatile boolean mRun; private Printer mLogging = null; private static Looper mMainLooper = null; // guarded by Looper.class
- ThreadLocal主要是用於存放looper,如果沒有調用prepare()方法的話,使用sThreadLocal的get方法會返回空。具體原因會在後面代碼裡具體說明。關於ThreadLocal,在這多說兩句:ThreadLocal很容易讓人望文生義,想當然地認為是一個“本地線程”。其實,ThreadLocal並不是一個Thread,而是Thread的局部變數,也許把它命名為ThreadLocalVariable更容易讓人理解一些。
當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的線程提供獨立的變數副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
從線程的角度看,目標變數就像是線程的本地變數,這也是類名中“Local”所要表達的意思。
- 一個MessageQueue,用於存放訊息,詳情請看:MessageQueue源碼解析
- 和這個looper相關的線程
- looper是否開啟,注意他的關鍵字:volatile:Volatile修飾的成員變數在每次被線程訪問時,都強迫從共用記憶體中重讀該成員變數的值。而且,當成員變數發生變化時,強迫線程將變化值回寫到共用記憶體。這樣在任何時刻,兩個不同的線程總是看到某個成員變數的同一個值。Java語言規範中指出:為了獲得最佳速度,允許線程儲存共用成員變數的私人拷貝,而且只當線程進入或者離開同步代碼塊時才與共用成員變數的原始值對比。這樣當多個線程同時與某個對象互動時,就必須要注意到要讓線程及時的得到共用成員變數的變化。而volatile關鍵字就是提示VM:對於這個成員變數不能儲存它的私人拷貝,而應直接與共用成員變數互動。使用建議:在兩個或者更多的線程訪問的成員變數上使用volatile。當要訪問的變數已在synchronized代碼塊中,或者為常量時,不必使用。由於使用volatile屏蔽掉了VM中必要的代碼最佳化,所以在效率上比較低,因此一定在必要時才使用此關鍵字。
- 一個printer類型的mLogging。看下printer:一個用於列印資訊的簡單介面。
Simple interface for printing text, allowing redirection to various targets. Standard implementations are android.util.LogPrinter, android.util.StringBuilderPrinter, and android.util.PrintWriterPrinter.
- 主線程(UI線程)looper。(在UI線程中系統會自動建立looper,其原理會在下面代碼中進行解析)
看完變數,再看構造方法:
private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }
初始設定變數。
/** 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() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); }
看一下prepare類,首先它是一個靜態類。為當前線程初始化一個looper,在正式啟動這個loop的之前,你有機會建立一個handler和這個looper關聯。在調用loop方法之前,一定要確認調用prepare方法,在最後結束的時候調用quit方法。如果這個線程已經設過了looper的話就會報錯這說明,一個線程只能設一個looper。
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(); setMainLooper(myLooper()); myLooper().mQueue.mQuitAllowed = false; }
接下來是prepareMainLooper方法,為應用UI線程初始化一個looper,這個主looper是由android環境自動建立,所以不需要你調用這個方法。方法內部調用了prepare方法為線程初始化一個looper。最後那句代碼是把queue的允許退出設為false。
然後調用setMainLooper這個方法為mMainLooper賦值
private synchronized static void setMainLooper(Looper looper) { mMainLooper = looper; }
而myLooper方法:
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }
就是為了擷取前面prepare方法初始化的looper。這個方法返回與當前線程想關聯的Looper實體,如果調用這個方法的線程沒有與looper相關聯,則返回空。
下面一個方法是擷取MainLooper:
/** Returns the application's main looper, which lives in the main thread of the application. */ public synchronized static Looper getMainLooper() { return mMainLooper; }
返回在主線程裡存在的主looper。
下面是本篇代碼之重點:loop方法:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { Looper me = myLooper();//擷取當前looper if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); }//如果為空白,則拋異常 MessageQueue queue = me.mQueue;//把當前looper的queue賦值給局部變數queue // 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();//確保當前線程屬於當前進程,並且記錄真實的token。 final long ident = Binder.clearCallingIdentity(); while (true) { Message msg = queue.next(); // might block有可能會阻塞 if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message.退出訊息的標示就是target為空白 return; } long wallStart = 0; long threadStart = 0; // This must be in a local variable, in case a UI event sets the logger 一個局部變數,為ui事件設定log記錄。 Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); wallStart = SystemClock.currentTimeMicro(); threadStart = SystemClock.currentThreadTimeMicro(); } //handler處理訊息 msg.target.dispatchMessage(msg); if (logging != null) { long wallTime = SystemClock.currentTimeMicro() - wallStart; long threadTime = SystemClock.currentThreadTimeMicro() - threadStart; logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); if (logging instanceof Profiler) { ((Profiler) logging).profile(msg, wallStart, wallTime, threadStart, threadTime); } } // 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); } //處理完成後,調用Message.recycle()將其放入Message Pool中。 msg.recycle(); } } }
這個方法,主要講解在注釋中。
/** * Control logging of messages as they are processed by this Looper. If * enabled, a log message will be written to <var>printer</var> * at the beginning and ending of each message dispatch, identifying the * target Handler and message contents. * * @param printer A Printer object that will receive log messages, or * null to disable message logging. */ public void setMessageLogging(Printer printer) { mLogging = printer; }
設定使用looper處理訊息時的log,如果啟用,在每條訊息指派開始和結束的日誌訊息將被寫入到Printer類,以確定目標Handler和訊息內容。
/** * Return the {@link MessageQueue} object associated with the current * thread. This must be called from a thread running a Looper, or a * NullPointerException will be thrown. */ public static MessageQueue myQueue() { return myLooper().mQueue; }
返回與當前線程相關聯的MessageQueue。當Looper運行時,該方法一定會被調用。
public void quit() { Message msg = Message.obtain(); // NOTE: By enqueueing directly into the message queue, the // message is left with a null target. This is how we know it is // a quit message. mQueue.enqueueMessage(msg, 0); }
退出方法,往訊息佇列中加一個空msg。這就是約定的退出訊息,在處理該訊息時,looper就會退出。
返回當前線程和返回當前Messagequeue:
/** * Return the Thread associated with this Looper. */ public Thread getThread() { return mThread; } /** @hide */ public MessageQueue getQueue() { return mQueue; }
在往後就是列印列印,異常定義的dump方法:
public void dump(Printer pw, String prefix) { pw = PrefixPrinter.create(pw, prefix); pw.println(this.toString()); pw.println("mRun=" + mRun); pw.println("mThread=" + mThread); pw.println("mQueue=" + ((mQueue != null) ? mQueue : "(null")); if (mQueue != null) { synchronized (mQueue) { long now = SystemClock.uptimeMillis(); Message msg = mQueue.mMessages; int n = 0; while (msg != null) { pw.println(" Message " + n + ": " + msg.toString(now)); n++; msg = msg.next; } pw.println("(Total messages: " + n + ")"); } } }
最後面還有一個有關log的介面:
/** * @hide */ public static interface Profiler { void profile(Message message, long wallStart, long wallTime, long threadStart, long threadTime); }
好,結束,可以做飯,然後重溫最愛的《肖申克的救贖》了。
查看更多源碼內容:Android源碼解析!