功能劃分
Handler的使用必須與幾個組件一起。
*Message: Handler接收和處理的消息對象,類似於一個業務類,封裝了一些變量。
*MessageQueue:一個隊列容器,採用先進先出的原則管理Message。程序創建Looper對象的時候會在構造函數中創建MessageQueue對象。
*Looper:負責管理MessageQueue和Message對象,讀取到MessageQueue中的Message之後就會採用sendMessage的方式把消息發送給對應(發送該消息)的Handler來處理。
要在一個線程中使用Handler,則該線程中必須要有一個MessageQueue對象。而MessageQueue是由Looper來管理的。所以在一個線程中必須要有一個Looper對象,而且每個線程只能有一個Looper對象。
而根據線程的不同分類,Handler的使用就大致可以分爲兩種情況。
UI線程使用Handler
分析
在UI線程中無需創建Looper對象,因爲在應用啓動,UI線程啓動的時候 系統已經自動創建了一個Looper對象。
這裏就要提到一個類ActivityThread,這個類是程序啓動的入口
下面是該類的部分源碼
public final class ActivityThread {
......
public static final void main(String[] args) {
......
Looper.prepareMainLooper();
......
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
Looper.loop();
......
thread.detach();
......
}
}
其中可以看到該類中的main函數,這個函數就是進程的入口函數。在main函數中調用了 Looper.prepareMainLooper();
就是在當前線程中創建Looper對象,所以在UI線程中我們是不需要 手動創建Looper的。
接下來再看Looper的源碼。
public final class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
/** 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));
}
這裏面的public 修飾的prepare()方法會調用private的prepare方法,然後去檢查當前線程是否有Looper對象,沒有則創建,如果當前線程已經有了Looper對象,再次調用Looper.prepare()方法的話,會拋出異常的。
這裏的ThreadLocal可以把它理解成一個容器吧。關於該類的詳細信息,可以自己查看源碼。
總結
在UI線程中要使用Handler,只需要創建Handler對象,重寫Handler中的 handlerMessage方法,發送消息 即可。
子線程(非UI線程)使用Handler
分析
在子線程中使用Handler,我們必須手動爲當前線程創建一個Looper對象,調用Looper.prepare()方法即可。
創建完成之後記得啓動Looper的輪詢,調用Looper.loop()方法。
/**
* 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方法可以看出,它會無線循環檢查MessageQueue。
總結
在子線程中使用Handler
1.需要手動爲當前線程創建Looper對象
2.創建Handler對象,重寫handlerMessage方法
3.調用Looper的loop方法啓動Looper
這裏只是簡單的介紹一下 三者之間的關係,與工作原理。Handler是系統通信的重要組件,它的用法遠不止這些,更深層次的用法。可以查看羅昇陽前輩的博文