一、什么是消息循環(huán)
消息循環(huán)概述:Android的消息循環(huán)是針對線程的瞎领,每個線程都可以有自己的消息隊列和消息循環(huán)蝙砌,通過消息循環(huán)可實現(xiàn)線程間通信。在Android線程內(nèi)松忍,可以通過消息循環(huán)的機制以隊列的方式實現(xiàn)消息的發(fā)送蒸殿,處理等工作
作用:線程間通信
涉及到的核心類:
Message:消息的實體的封裝
Handler:消息的發(fā)送和處理
Looper:消息循環(huán)的核心,管理消息隊列鸣峭,實現(xiàn)與當(dāng)前線程綁定
MessageQueue:消息隊列
二宏所、消息循環(huán)的過程
消息循環(huán)可以分為兩部分來講
- 給線程開啟消息循環(huán)
class WorkThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// 處理收到的消息
}
};
Looper.loop();
}
}
WorkThread myWorkThread = new MyThread2();
myWorkThread.start();
- 創(chuàng)建Message,并發(fā)送Message摊溶,處理Message
Message msg = Message.obtain();
msg.what = what;
msg.obj = strMsg;
myWorkThread.mHandler.sendMessage(msg);
二爬骤、消息循環(huán)的原理
開啟線程的消息循環(huán)
如上代碼,就開始了WorkThread線程的消息循環(huán)更扁,在主線程或者其他子線程盖腕,就可以通過WorkThread.mHandler向WorkThread線程發(fā)送Message赫冬,并且由handleMessage最終處理Message
我們按照代碼的步驟來看主要就是做了3個步驟
- Looper.prepare()
- new Handler()
- Looper.loop()
1. Looper.prepare()
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
final Thread mThread;
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));
}
private Looper(boolean quitAllowed) {
//初始化了MessageQueue
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
代碼很簡單浓镜,可以看出prepare()就是
- new了一個Looper對象溃列,同時初始化了MessageQueue。
- 把該Looper對象放入了創(chuàng)建它的當(dāng)前線程的ThreadLocal里膛薛,建立Looper與創(chuàng)建它的線程的關(guān)聯(lián)(ThreadLocal不理解的同學(xué)听隐,可以先查看一些ThreadLocal的資料)『遄模可以理解為Looper成為了創(chuàng)建它的當(dāng)前線程的一個本地變量
放到前面代碼里雅任,就是為WorkThread線程創(chuàng)建了一個本地變量Looper,可以在WorkThread線程中用Looper.myLooper()來獲取該Looper對象
2. 創(chuàng)建Handler
我們來看下Handler的構(gòu)造函數(shù)
public Handler() {
this(null, false);
}
public Handler(Looper looper) {
this(looper, 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());
}
}
//獲取創(chuàng)建線程的Looper
mLooper = Looper.myLooper();
//沒有Looper咨跌,則直接拋出異常
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;
}
由上面代碼可以看出
new Handler()的時候必須先給handler指定一個Looper(在主線程中不需要指定沪么,因為主線程的消息循環(huán)在程序啟動時就已經(jīng)開啟了),否則會拋出異常锌半,即Handler使依附于Looper的
3. Looper.loop()
public static void loop() {
......//省略
for (;;) {
Message msg = queue.next(); //可能阻塞 MessageQueue中取出msg
......//省略
msg.target.dispatchMessage(msg);//分發(fā)msg
......//省略
msg.recycleUnchecked();//回收msg
}
}
代碼比較長禽车,我們挑核心來看,其實主要就是做了三件事
- 開啟了一個死循環(huán)for(;;)
- 從MessageQueue中取出Message
- 分發(fā)Message
- 回收Message
由于這是一個死循環(huán)刊殉,這個循環(huán)會不停的調(diào)用queue.next()殉摔,從MessageQueue中取出Message來處理,然后調(diào)用dispatchMessage來分發(fā)Message记焊,最后回收msg
這樣一個線程的消息循環(huán)就建立了逸月,其他線程就可以通過該線程的handler與該線程進行通信
創(chuàng)建Message,并發(fā)送Message遍膜,處理Message
1. 創(chuàng)建Message
我們在Looper.loop()中知道碗硬,Message在發(fā)送后會被回收,那么我們一起看下Message的構(gòu)造函數(shù)和回收函數(shù)
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}
private static final Object sPoolSync = new Object();
private static Message sPool;//單鏈表結(jié)構(gòu)的頭部引用
Message next;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;//復(fù)用Message
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
//sPool為null 則新建
return new Message();
}
void recycleUnchecked() {
//重置了Message的參數(shù)
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//將Message以單鏈表的方式存儲
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
//sPool指向單鏈表的頭部
sPool = this;
sPoolSize++;
}
}
}
由代碼可以看出Message構(gòu)造函數(shù)雖然是public的瓢颅,但是官方建議我們用obtain()來或者Message恩尾。由obtain()和recycleUnchecked()代碼可以看出Message采用一個單鏈表結(jié)構(gòu)的對象池,來復(fù)用Message對象惜索。
2. 消息發(fā)送handler.sendMessage和處理
看源碼可知特笋,handler.sendMessage的幾個重載的方法,最終是調(diào)用了sendMessageAtTime方法巾兆,而sendMessageAtTime又調(diào)用了enqueueMessage方法猎物,而enqueueMessage方法最終是調(diào)用了MessageQueue的enqueueMessage方法,把Message加入到了消息隊列中
偽代碼調(diào)用邏輯如下
handler.sendMessage(Message msg){
sendMessageAtTime(Message msg, long uptimeMillis){
enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){
MessageQueue.enqueueMessage(msg, uptimeMillis)
}
}
}
然后從Looper.loop()中可以看到角塑,Message在被發(fā)送到MessageQueue中后蔫磨,loop()中開啟的死循環(huán)是調(diào)用了MessageQueue.next()方法取出了Message,并調(diào)用msg.target.dispatchMessage(msg)方法來分發(fā)msg圃伶,而msg.target就是handler堤如,那么看下handler的dispatchMessage方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
public interface Callback {
public boolean handleMessage(Message msg);
}
private static void handleCallback(Message message) {
message.callback.run();
}
可以看出dispatchMessage有3中邏輯
- 調(diào)用了message.callback.run()蒲列,來處理message
runOnUiThread(),handler.post(Runnable r)就是這樣處理的 - handler的Callback接口方式來處理消息,在創(chuàng)建handler的時候可以指定一個mCallback來處理消息
- handler.handleMessage來最終處理消息
這樣從消息循環(huán)的開啟搀罢,到消息的發(fā)送蝗岖,處理就完成了
總結(jié)
- Looper是和當(dāng)前線程關(guān)聯(lián)的,Looper.prepare()建立與調(diào)用它的當(dāng)前線程的關(guān)聯(lián)榔至,Looper.loop()開啟當(dāng)前線程消息循環(huán)
- handler是和Looper關(guān)聯(lián)的抵赢,創(chuàng)建handler的時候必須指定其Looper,而主線程由于在程序開啟時就開啟了它的消息循環(huán)唧取,所以只有在主線程創(chuàng)建handler的時候铅鲤,不需要指定其Looper
- 創(chuàng)建Message,用Message.obtain()枫弟,可以復(fù)用Message邢享,不要直接new。
- 在子線程中開啟的消息循環(huán)淡诗,可調(diào)用Looper.myLooper().quit()骇塘,來退出消息循環(huán),主線程的消息循環(huán)不可退出