android Looper 原始碼解析
android Looper 官網解釋:
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.
翻譯成中文:
Looper 類,用於執行執行緒的訊息迴圈。執行緒預設情況下沒有與它們相關聯的訊息迴圈;要建立一個執行緒,需要線上程中準備執行迴圈,然後迴圈執行程序訊息,直到迴圈停止。
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.
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();
}
}
通過上面的示例程式碼可以看出Looper的主要使用場景 —非主執行緒需要Handler傳送訊息。
在上述示例中提到了Looper中兩個重要的方法 perpare()、loop()
/** 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);
}
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物件,而該物件是一個執行緒內部的資料儲存類,具體的內部原理可以參考:
同時在該方法中創建出了一個Looper物件:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在Looper的構造方法中創建出一個新的MessageQueue物件,該物件的內部原理可以參考:
xxxxxxx
至此可以看出當Looper呼叫prepare()方法時,實質上只是創建出一個MessageQueue佇列。
示例demo中在呼叫prepare()方法之後,緊接著呼叫了 Looper.loop()方法,該方法在Looper.java 中的示例原始碼:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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();
}
}
loop方法主要是:線上程中執行訊息佇列,並從訊息佇列中獲取訊息Message:對於Message的內部實現原理可以參考:
xxxxx
下面來具體分析loop方法的具體實現。
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
public static Looper myLooper() {
return sThreadLocal.get();
}
在呼叫loop方法時,會首先判斷當前執行緒中是否已存在looper物件,如果當前執行緒中looper物件為null 則丟擲異常:No Looper; Looper.prepare() wasn’t called on this thread.
final MessageQueue queue = me.mQueue;
之後通過looper物件 獲取MessageQueue物件。
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
以上兩行程式碼在Binder.java 中可以看出實質是通過jni呼叫了底層處理,具體處理暫時無法獲取,通過註釋
/**
* Reset the identity of the incoming IPC on the current thread. This can
* be useful if, while handling an incoming call, you will be calling
* on interfaces of other objects that may be local to your process and
* need to do permission checks on the calls coming into them (so they
* will check the permission of your own local process, and not whatever
* process originally called you).
*
* @return Returns an opaque token that can be used to restore the
* original calling identity by passing it to
* {@link #restoreCallingIdentity(long)}.
*
* @see #getCallingPid()
* @see #getCallingUid()
* @see #restoreCallingIdentity(long)
*/
public static final native long clearCallingIdentity();
可以看出clearCallingIdentity() 方法的目的是:確保此執行緒的標識是本地程序的標識,並跟蹤此執行緒本地標識。
同時在loop方法的for(;;) 迴圈中看到
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);
}
每次迴圈 都會檢查 此執行緒的本地標識是否一致,如果不一致 則會執行Log列印否則 不會執行。
緊接著來細看loop中的for(;;)迴圈具體做了什麼?
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();
}
for (;;) 代表一個無休止迴圈。如果迴圈中沒有終止,那麼該迴圈將會一直迴圈下去。
在該迴圈中 只需要關注這幾行程式碼即可,而像另外的幾行程式碼,在if條件成立的情況下是一個logging日誌的列印。
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
.....
msg.target.dispatchMessage(msg);
....
msg.recycleUnchecked();
上述幾行程式碼可以看到 通過MessageQueue的next()方法獲取到佇列中的Message訊息,如果訊息為null 則跳出迴圈條件
如果不為null 那麼直接呼叫 msg.target = Handler 物件的dispatchMessage(msg) 方法
最後 直接呼叫Message物件的recycleUnchecked()方法,而該方法主要是對訊息進行回收。
至此可以看出loop方法的主要作用是一個無限for迴圈,在該迴圈中如果MessageQueue中訊息不為null,那麼將會從該佇列中一直去取Message,並將該Message通過引數方法傳遞給Handler。
下面在介紹一下Looper.java中其它的一些方法:
public static MessageQueue myQueue() {
return myLooper().mQueue;
}
獲取當前管理的MessageQueue物件
/**
* 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(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
為當前執行緒初始化一個looper物件,並將該looper物件標記為application main looper
/** Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
獲取主執行緒的Looper物件
Looper是通過呼叫loop方法驅動著訊息迴圈的進行: 從MessageQueue中阻塞式地取出一個訊息,然後讓Handler處理該訊息,周而復始,loop方法是個死迴圈方法。
那如何終止訊息迴圈呢?我們可以呼叫Looper的quit方法或quitSafely方法,二者稍有不同
/**
* Quits the looper.
* <p>
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
當我們呼叫Looper的quit方法時,實際上執行了MessageQueue中的removeAllMessagesLocked方法,該方法的作用是把MessageQueue訊息池中所有的訊息全部清空,無論是延遲訊息(延遲訊息是指通過sendMessageDelayed或通過postDelayed等方法傳送的需要延遲執行的訊息)還是非延遲訊息。
/**
* Quits the looper safely.
* <p>
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p>
*/
public void quitSafely() {
mQueue.quit(true);
}
當我們呼叫Looper的quitSafely方法時,實際上執行了MessageQueue中的removeAllFutureMessagesLocked方法,通過名字就可以看出,該方法只會清空MessageQueue訊息池中所有的延遲訊息,並將訊息池中所有的非延遲訊息派發出去讓Handler去處理,quitSafely相比於quit方法安全之處在於清空訊息之前會派發所有的非延遲訊息。
注:無論是呼叫了quit方法還是quitSafely方法只會,Looper就不再接收新的訊息。即在呼叫了Looper的quit或quitSafely方法之後,訊息迴圈就終結了,這時候再通過Handler呼叫sendMessage或post等方法傳送訊息時均返回false,表示訊息沒有成功放入訊息佇列MessageQueue中,因為訊息佇列已經退出了。
需要注意的是Looper的quit方法從API Level 1就存在了,但是Looper的quitSafely方法從API Level 18才新增進來。
至此Looper.java的原始碼解析已經解析完成,如果不明白的地方讀者可留言,或者從android原始碼中自行理解。