Android的訊息機制(Handler的工作原理)
Android的訊息機制
Android中的訊息機制其實也就是Handler
的執行機制。Android中通過使用Handler
來進行更新UI的操作。因為Android的UI更新是單執行緒模型,UI控制元件也都是非執行緒安全的。其原因是如果給UI控制元件加鎖,那麼效率將會變得底下的同時還會將UI訪問的邏輯變得複雜。
Handler的執行基於MessageQueue
和Looper
的支撐。MessageQueue
顧名思義是訊息佇列。但其實是一個單項鍊表結構來儲存資訊Message
的。而Looper
則是不斷去讀取訊息佇列MessageQueue
中的資訊,將其交與傳送該訊息的Handler
去dispatchMessage
下面我們就Handler
、MessageQueue
和Looper
分別分析一下其各自的工作原理。
MessageQueue
訊息佇列中維護了一個Message
型別的變數mMessages
,Message
是連結串列結構,所以之前所說訊息佇列是通過單項鍊表結構來儲存訊息。
當訊息佇列收到來自Handler的訊息時,訊息佇列需要將此條訊息插入佇列中也即mMessages
中。訊息插入順序和它的執行之間相關。
boolean enqueueMessage(Message msg, long 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{
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWeke = 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()){
neesWake = false;
}
}
msg.next = p; ///把訊息插入到訊息佇列的頭部
prev.next = msg;
}
if(neesWake){
nativeWake(mPtr);
}
}
return true;
}
主要是進行了訊息的單鏈表插入,現在我們再來看下新的函式讀取函式`next()。
...
for(;;){
...
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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
}
next()
方法中有一個無線迴圈,如果沒有訊息,next()
會一直阻塞。知道有訊息來,會返回這條訊息並從訊息佇列中移除。
在分析完了訊息佇列的插入和讀取訊息之後,我們應該再看下Looper
,但是在分析Looper
之前,我們需要先對ThreadLocal
有所瞭解。
ThreadLocal
ThreadLocal是一個執行緒內部的資料儲存類。通過它可以在指定的執行緒記憶體儲資料,其他執行緒無法訪問到該執行緒的資料。當作用域是執行緒的時候可以使用ThreadLocal
。作用有兩個:1.在指定執行緒儲存資料。2.複雜邏輯下的物件傳遞。
ThreadLocal
會從各自執行緒內部取出一個數組,然後再從陣列中根據當前ThreadLocal
的索引去查找出對應的Value的值。
public void set(T value){
Thread currentThread = Thread.currentThread();
Values valuse = Values(currentThread);
if(valuse == null){
initializeValues(currentThread);
}
values.put(this, value);
}
可以看到,通過當前執行緒拿到陣列Values
,如果沒有,則傳入當前執行緒初始化一個數組。然後將值放到陣列中。我們再看下是如何將值放到陣列中的。
private void set(ThrealLocal<?> key, Object value){
...
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
可以看到,ThreadLocal
的值儲存位置永遠都是ThreadLocal
的欄位i
(值的hashCode
和長度減1的值做位運算)的下一個位置。
get
方法則是找出欄位i
的值,然後從陣列的i
的下一位獲取到ThreadLocal
的值。
好了,講解完了ThreadLocal
之後,我們需要分析一下Looper
的原理了。Looper
在訊息機制中扮演訊息迴圈的角色。通過不斷的Loop()
去處理訊息佇列中的訊息。我們先來分析Looper
的建構函式。
prite Looper(boolean quitAllowed){
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到,初始化Looper
的時候,會內部構建一個訊息佇列。我們可以看到這個建構函式是一個私有函式,那我們應該如何為一個執行緒建立一個Looper
呢。我們可以通過Looper.prepare()
為執行緒建立一個Looper
。然後再通過Looper.loop()
來開啟訊息迴圈。我們來看下looper()
函式是如何實現的。
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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()
中是一個死迴圈,只有當訊息佇列的next()
方法返回為null
的時候才會退出。當Looper
的quit
方法呼叫的時候,MessageQueue
的quit
或quitSafely
方法會通知訊息佇列退出,這個時候訊息佇列的next()
就會返回為null
。而沒有訊息的時候next()
會一直阻塞,這樣導致了loop
也一直阻塞。當有訊息的時候,Looper
則會通過訊息的target
找到傳送訊息的handler
然後呼叫其dispatchMessage(Message msg)
去將訊息給Handler
處理。而dispatchMessage
是在建立Handler
時所使用的Looper
中執行的。所以就將邏輯程式碼轉移到了指定的執行緒上執行了。
好了,大致分析了Looper
之後,我們再接著對Handler
做一個簡單的瞭解。
Handler
Handler的工作主要是傳送和接收訊息,也即sendMessage
和dispatchMessage
兩個函式。
在sendMessage
是將訊息佇列中插入一條訊息,即呼叫enqueMessage
。MessageQueue
收到訊息之後呼叫next()
將訊息傳給Looper
處理,然後Looper
又將訊息丟給該Handler
來處理。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
其中callback
是一個介面,是用來例項化Handler
時候的一個Runnable
物件。
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
主執行緒的訊息迴圈
這裡稍微再講解一下主執行緒的訊息迴圈。Android的主執行緒UI執行緒也就是ActivityThread
。其main()
方法中系統會通過Looper.prepareMainLooper()
來建立主執行緒的Looper
以及MessageQueue
。通過loop()
來開啟主執行緒的訊息迴圈。ActivityThread
中有一個Handler
來進行訊息迴圈,這個Handler
就是AcitivytThread
內的ActivityThread.H
。
ActivityThread
通過ApplicationThread
和AMS
進行程序間通訊,AMS
以程序間通訊的方式完成ActivityThread
的請求後會回撥ApplicationThread
的Binder
方法,然後ApplicationThread
會向H
傳送訊息,H
接收到訊息後會將AplicationThread
中的邏輯切換到AcitivyThread
中執行,也就是切換到主執行緒執行,這個過程就是主執行緒的訊息迴圈模型。
以上摘選自任玉剛的《Android開發藝術探索》加上自己的一丟丟記憶理解。