前言
在 Android 程式設計當中,一般是不可以在子執行緒中更新主執行緒的 UI ,這時候 Android 給我們提供了一套在子執行緒中更新 UI 的訊息機制,即 Handler 訊息處理機制。
Handler 允許你傳送程式訊息和可執行的物件到關聯的執行緒訊息佇列中。每個 Handler 的例項關聯一個執行緒和這個執行緒的訊息佇列。當建立一個 Handler 的時候,系統會自動把這個 Handler 例項繫結到當前建立這個 Handler 的執行緒中。
Handler的基本使用(示例)
[1.0] 在主執行緒中建立一個 Handler 的例項,並重寫 Handler 的 handleMessage(Message msg) 方法
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
textView.setText((String)msg.obj);
}
};
[2.0] 在主執行緒中建立一個子執行緒,並通過 Handler 的例項呼叫 sendMessage 給主執行緒傳送訊息
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.obj = "aaa";
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendMessage(message);
}
}).start();
複製程式碼
上面程式碼功能是實現一個啟動應用程式 3 秒鐘或更新 TextView 控制元件的內容。下面進行 Handler 的原始碼分析,Handler 是如何實現上面的邏輯的:也就是 Handler 通過 sendMessage(message) 把一個 Message 例項如何傳送給對應的 Handler 的 handleMessage(Message msg) 的
:
示例原始碼分析
下面先從 Message 的入口,也就是 sendMessage(Message msg)
方法開始分析。通過檢視 sedMessage(Message msg)
的方法原始碼,可知道最後會呼叫到 sendMessageAtTime(Message msg, long uptimeMillis)
方法:
[原始碼 1]
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue; // 1
...
return enqueueMessage(queue, msg, uptimeMillis);
}
複製程式碼
上面的 [原始碼 1],最重要的需要明白註釋 1 處的 mQueue
是來自哪裡的訊息佇列 MessageQueue
,也就是在 Handler 的類中,它是如何初始化拿到例項的。因為在上面的示例原始碼中的 Handler 例項重寫的 handleMessage(Message msg)
是被 dispatchMessage(Message msg)
中呼叫的,如下面的 [原始碼 2] 的註釋 1 處:
[原始碼 2]
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); // 1
}
}
複製程式碼
上面的 dispatchMessage(Message msg)
方法的主要的作用是處理系統的訊息的,也可以看作分發系統的訊息。好的,我們的重點不在這裡,那麼是誰呼叫了 dispatchMessage(Message msg)
這個方法呢?通過檢視原始碼我們會發現:
[原始碼 3]
public static void loop() {
final Looper me = myLooper(); // 2
...
final MessageQueue queue = me.mQueue;
...
for (;;) { // 3
Message msg = queue.next();
...
Printer logging = me.mLogging;
...
msg.target.dispatchMessage(msg); // 1
...
// 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();
}
}
複製程式碼
在上面的 [原始碼 3] 中的註釋 1 處呼叫了 dispatchMessage(Message msg)
方法。在註釋 2 處,通過呼叫 myLooper()
獲取當前執行緒的 Looper 物件,而該 Looper 物件攜帶著當前執行緒的訊息佇列(注意:是當前建立 Handler 的執行緒),而在註釋 3 處,for死迴圈主要的作用是把 queue
這個訊息佇列裡面的 Message
訊息取出,然後通過 msg.target.dispatchMessage(msg)
把訊息分發出去,也就是呼叫了 [原始碼 2] 的 dispatchMessage(msg)
方法。閱讀到這裡的你,可能心裡面有個咕嘟:為什麼通過上面 [原始碼 3] 的註釋 2 處呼叫的 myLooper()
方法就可以獲取到當前執行緒的訊息佇列呢?
好的,在 Android 系統原始碼裡面最精彩的部分:在 Handler 的建構函式裡面:
[原始碼 4]
public Handler() {
this(null, false);
}
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());
}
}
mLooper = Looper.myLooper(); // 1
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; // 3 當前執行緒的 Looper 物件所持有的訊息佇列 (mLooper.mQueue 為 final 型別)
mCallback = callback;
mAsynchronous = async;
}
複製程式碼
在上面的 [原始碼 4]中的註釋 1 處,可以看到,也是同樣地獲取當前執行緒的 Looper 例項 mLooper,然後在註釋 3 處,把 final 型別的訊息佇列例項賦給 mQueue
這個全域性變數。到了這裡,在 Handler 的建構函式裡面初始化了訊息佇列 MessageQueue
,因此,在 [原始碼 1] 中的 mQueue
便是當前執行緒 Looper 物件所持有的訊息佇列:
[原始碼 1]
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue; // 1
...
//queue 訊息佇列
//msg 需要傳遞傳送的 message
//uptimeMillis 當前訊息傳送的時間
return enqueueMessage(queue, msg, uptimeMillis);
}
...
[原始碼 5]
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //把當前的 Handler 的例項賦給 target(private Handler target)
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); // 把訊息送進訊息佇列
}
複製程式碼
到了這裡,[原始碼 1] 裡面呼叫的 enqueueMessage(queue, msg, uptimeMillis)
方法,其邏輯如上面的 [原始碼 5],把需要傳送的 Message 傳送給了 Looper.myLooper().mQueue 這個訊息佇列(MessageQueue 是利用類似於單連結串列的方式, 以 Message 為節點來儲存 msg 的,即以連結串列的方式構造佇列);
閱讀到這裡,你可能會比較疑惑,為什麼可以直接通過 Looper.myLooper()
方法獲取到 Looper 的例項呢? 其實,Android 應用程式的 Activity
例項是由 ActivityThread
建立的,同時,ActivityThread
也會預設去建立主執行緒,也會建立主執行緒的 Looper
的例項。
小結
對上面原始碼的總結,主要就是: Handler 負責把子執行緒中傳進來的訊息(Message),傳遞給 Looper,由 Looper 把訊息封裝成訊息佇列,然後再有 Handler 從訊息佇列中把訊息分發到子執行緒外面。
哈哈,這次的 Handler 事件處理機制的原理解析就到這裡結束了,感謝你的閱讀...