異步消息處理
一、Looper
Looper負責的就是創(chuàng)建一個MessageQueue,然后進入一個無限循環(huán)體不斷從該MessageQueue中讀取消息,而消息的創(chuàng)建者就是一個或多個Handler 。
在構(gòu)造方法中,創(chuàng)建了一個MessageQueue(消息隊列)。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
方法1. static void prepare() 靜態(tài)的方法,準備
ThreadLocal中添加進一個新的對象,sThreadLocal是一個ThreadLocal對象,可以在一個線程中存儲變量。prepare()中判斷了當前線程的 Looper 對象是否為null,不為 null則拋出異常。這也就說明了一個線程Looper.prepare()方法不能被調(diào)用兩次,同時也保證了一個線程中只有一個Looper實例
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
方法2. loop() 方法
public static Looper myLooper() {
return sThreadLocal.get();
}
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();
// 無限循環(huán)階段
for (;;) {
Message msg = queue.next(); // 可能阻塞,只有當 MessageQueue 調(diào)用 quit 方法時,next 方法才會返回 null,否則都會阻塞等待
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);
}
//使用調(diào)用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。
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.recycle();
}
}
Looper 的靜態(tài)方法 prepare() 中新建了一個 Looper 對象,Looper 的構(gòu)造方法中初始化了 MessageQueue(新建) 、
且為 Thread 對象賦值(當前線程)。這兩個對象是 Looper 對象持有的,每個 Looper 對象中都有一個 MessageQueue 和 當前線程對象,
prepare() 中將新建的 Looper 對象添加到了 ThreadLocal<Looper> 對象中,這個對象是
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
ThreadLocal<Looper> 在類的最初就會實例化,在存儲 Looper 的時候會以當前線程的對象作為一個參數(shù),在取Looper
時也會通過當前線程來取
prepare() 方法中會判斷當前線程是否已經(jīng)綁定了 Looper 對象,如果綁定了則會拋出
**throw new RuntimeException("Only one Looper may be created per thread"); **
在初始化 Handler 時也會根據(jù)當前線程判斷是否綁定了 Looper ,如果沒有綁定則會拋出
throw new IllegalStateException("No Looper; Looper.prepare() wasn't called on this thread.");
通過這個過程,保證了每個線程只能綁定一個 Looper ,并且每個 Looper 都有一個 MessageQueue 對象
Looper.myLooper()獲取了當前線程保存的 Looper 實例,然后在又獲取了這個 Looper 實例中保存的
MessageQueue(消息隊列),這樣就保證了 handler 的實例與我們 Looper 實例中 MessageQueue 關(guān)聯(lián)上了。
Looger.getMainLooper(); 獲取主線程的 Looper 對象,主線程會自動調(diào)用 Looper.prepareMainLooper() 方法
完成 Looper 對象的綁定,并將該 Looper 對象賦值給一個靜態(tài)的 Looper 變量,在調(diào)用 getMainLooper() 方法
時將該 Looper 對象返回。
Looper.myLooper() 方法是獲取當前線程的 Looper 對象。
MessageQueue 的 next() 方法
MessageQueue 的 next() 方法中,除了取出 Java 層需要處理的 Message 對象,還會調(diào)用 nativePollOnce() 方法處理 Native 層的消息循環(huán)
native 層的消息循環(huán)在 MessageQueue 構(gòu)造函數(shù)執(zhí)行時啟動,其構(gòu)造函數(shù)會在 Native 層創(chuàng)建 Native.Looper 和 NativeMessageQueue,通過 Linux 的管道和 epoll 機制來實現(xiàn)
Linux 的管道可以看做是一個文件,并且包含讀和寫端,epoll 機制是如果一個線程讀該文件時該文件沒有內(nèi)容,則該線程會進入等待,當另一個線程往該文件寫內(nèi)容后會喚醒正在讀端等待的線程。這個等待和喚醒的機制就使用 Linux 的 epoll 機制
當 Java 層的 MessageQueue 獲取消息的時候,也會調(diào)用 natice 層的 MessageQueue 從管道中讀消息并且處理消息
創(chuàng)建 Native 層循環(huán)模型的原因:
Android 是支持純 Native 開發(fā)的,并且 Android 系統(tǒng)的核心組件也是運行在 native 層的,各組件直接需要通信,所以建立兩套循環(huán)機制就很重要
Looper主要作用:
與當前線程綁定,保證一個線程只會有一個 Looper 實例,同時一個 Looper 實例也只有一個 MessageQueue
loop() 方法,不斷從 MessageQueue 中去取消息,交給消息的 target 屬性的 dispatchMessage 去處理
好了,我們的異步消息處理線程已經(jīng)有了消息隊列(MessageQueue),也有了 Looper 在無限循環(huán)體中取出消息,
現(xiàn)在缺的就是發(fā)送消息的對象了,發(fā)送消息的對象是 Handler
二、Handler
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();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler 對象中有一個 Looper 對象和一個 MessageQueue 對象,這兩個屬性都是final 的在創(chuàng)建一個 Handler 對象的時候,如果當前線程沒有綁定 Looper 對象,則會拋出異常 throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");所以在一個線程中創(chuàng)建 Handler 的時候當前線程必須調(diào)用 Looper.prepare(); 方法
然后看我們最常用的sendMessage方法,方法最后調(diào)用了sendMessageAtTime,在此方法內(nèi)部有直接
獲取 MessageQueue 然后調(diào)用了 enqueueMessage 方法,我們再來看看此方法: enqueue : 入隊
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage 中首先為 meg.target 賦值為this,【Looper的loop方法會取出每個msg然后交給 msg.target.dispatchMessage(msg) 去處理消息,也就是把當前的handler作為msg的 target 屬性。
最終會調(diào)用 queue 的 enqueueMessage 的方法,也就是說 handler 發(fā)出的消息,最終會保存到消息隊列中去。enqueueMessage 方法中使用 synchronize 鎖 MessageQueue 對象,保證不同線程插入數(shù)據(jù)時的同步問題。
Looper 會調(diào)用 prepare() 和 loop() 方法,在當前執(zhí)行的線程中保存一個 Looper 實例,這個實例會保存一個 MessageQueue 對象,然后當前線程進入一個無限循環(huán)中去,不斷從 MessageQueue 中讀取 Handler 發(fā)來的消息。然后再回調(diào)創(chuàng)建這個消息的 handler 中的 dispathMessage 方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); // 使用創(chuàng)建 Message 時構(gòu)造方法中的 Runnable 處理消息
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { // 使用創(chuàng)建 Handler 時構(gòu)造方法中的 Handler.Callback 處理消息
return;
}
}
handleMessage(msg); // 重寫的處理消息方法處理
}
}
這個流程已經(jīng)解釋完畢,讓我們首先總結(jié)一下
首先Looper.prepare()在本線程中保存一個Looper實例,然后該實例中保存一個MessageQueue對象; 因為Looper.prepare()在一個線程中只能調(diào)用一次,所以MessageQueue在一個線程中只會存在一個。
Looper.loop()會讓當前線程進入一個無限循環(huán),不斷從 MessageQueue 的實例中讀取消息,然后回調(diào) msg.target.dispatchMessage(msg) 方法。
Handler 的構(gòu)造方法,會首先得到當前線程中保存的 Looper 實例,進而與 Looper 實例中的 MessageQueue 相關(guān)聯(lián)。
Handler的 sendMessage 方法,會給 msg 的 target 賦值為 handler 自身,然后加入 MessageQueue 中。
在構(gòu)造 Handler 實例時,我們會重寫 handleMessage 方法,也就是 msg.target.dispatchMessage(msg) 最終調(diào)用的方法。
好了,總結(jié)完成,大家可能還會問,那么在Activity中,我們并沒有顯示的調(diào)用 Looper.prepare() 和 Looper.loop() 方法,
為啥 Handler 可以成功創(chuàng)建呢,這是因為在 ActivityTherad 的 main() 方法中,已經(jīng)為主線程調(diào)用了Looper.prepareMainLooper() 和 Looper.loop() 方法。
關(guān)于Handler處理消息的方式
創(chuàng)建 Message 對象時,指定 Runnable 對象,
例如 Message.obtain(Handler handler, Runnable callback)創(chuàng)建 Handler.Callback 實現(xiàn)類對象,并作為創(chuàng)建 Handler 的構(gòu)造方法的參數(shù)
自定義類繼承自 Handler,并重寫 handlerMessage() 方法
以上3種方法的執(zhí)行順序:如果存在方式1,則由方式1直接處理;如果存在方式2,則方式2處理消息,且,如果方式2返回true,則處理完畢,否則,方式3也會處理消息。
Handler.post(); // 等多久執(zhí)行... run() 方法中的代碼
mHandler.post(new Runnable() {
@Override
public void run(){
og.e("TAG", Thread.currentThread().getName());
mTxt.setText("test");
}
});
然后 run 方法中可以寫更新UI的代碼,其實這個 Runnable 并沒有創(chuàng)建什么線程,而是發(fā)送了一條消息,下面看源碼:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到,在 getPostMessage 中,得到了一個 Message 對象,然后將我們創(chuàng)建的 Runable 對象作為 callback 屬性,賦值給了此 message.
注:產(chǎn)生一個Message對象,可以new ,也可以使用Message.obtain()方法;兩者都可以,但是更建議使用obtain方法,
因為Message內(nèi)部維護了一個Message池用于Message的復用,避免使用new 重新分配內(nèi)存。
最終和handler.sendMessage一樣,調(diào)用了sendMessageAtTime,然后調(diào)用了enqueueMessage方法,給msg.target賦值為 handler,最終加入MessagQueue.msg的 callback 和target都有值,那么會執(zhí)行哪個呢?
如果 callback 不為null,則執(zhí)行callback回調(diào),也就是我們的Runnable對象Handler 對象創(chuàng)建之后可以在任何線程中發(fā)消息,最終消息的處理都將回到 創(chuàng)建 Handler 時使用的 Looper 所在的線程處理。所以在子線程中可以使用主線程中創(chuàng)建的 Handler 對象發(fā)消息,在主線程中處理消息。
每個線程中可以創(chuàng)建多個 Handler ,處理消息時就調(diào)用 msg.targe 也就是 Handler 來處理,Looper 將消息發(fā)送給發(fā)送消息時使用的 Handler 對象來處理。子線程不可以直接創(chuàng)建 Handler, 必須在子線程先調(diào)用 Looper.prepare();線程中調(diào)用 Looper.loop(); 方法后才會開啟輪循
一個線程中只能有一個 Looper ,也就是只能有一個 MessageQueue ,可以有多個 Handler。一個 Handler 可以在不同的線程中發(fā)送消息,但是消息的處理都是在創(chuàng)建 Handler 時使用的 Looper 所在的線程
總結(jié)
每個線程在綁定 Looper 之后都有一個 MessageQueue ,MessageQueue 中存著需要處理的消息 Message,Message 中存著處理消息的 Handler,Handler 在發(fā)消息時會將消息發(fā)送到自己所在線程的 MessageQueue,Looper 的 loop 方法就是開啟 MessageQueue 的遍歷。這樣,Handler 所發(fā)的消息處理就會在 Handler 所在的線程中。
參考:http://blog.csdn.net/lmj623565791/article/details/47079737