簡介
在Android日常開發(fā)中,Handler的使用是非常高頻的。其類似于一種通信機制,這種機制主要靠Handler、Looper和MessageQueue的協(xié)同合作。
MessageQueue:消息隊列,先進先出,在獲取下一條消息的時候可能因為沒有消息從而阻塞。
Looper:通過ThreadLocal修飾,保證每一個線程都有一個獨立的Looper。工作開始后會處于一個無限循環(huán),不停地從MessageQueue取出消息,然后傳遞給Message指定的Handler處理。
Handler:投遞消息和處理消息,將生成一個新的消息Message,然后交給對應(yīng)Looper中的Message,等待Looper取出的消息進行回調(diào)處理。
Looper
//用于管理不同線程下不同的Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//UI線程的Looper,一般在整個進程開始的時候就創(chuàng)建了,后續(xù)可以直接使用
private static Looper sMainLooper;
//當前Looper持有的消息隊列
final MessageQueue mQueue;
//當前Looper關(guān)聯(lián)的線程
final Thread mThread;
Looper通過ThreadLocal保證在每一個線程中都有唯一且確認的Looper,內(nèi)部持有一個唯一的消息隊列和創(chuàng)建線程。
/**
* 準備Looper
*/
public static void prepare() {
prepare(true);//默認允許消息隊列退出
}
/**
* 準備Looper
* @param quitAllowed true允許MessageQueue通過quit退出,false當quit的時候會拋出異常
*/
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//當前線程已經(jīng)綁定有Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* 創(chuàng)建一個新的Looper
* @param quitAllowed
*/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//新建一個消息隊列
mThread = Thread.currentThread();//綁定當前線程
}
Looper不存在顯示的構(gòu)造方法,通過靜態(tài)方法prepare可以在當前線程中構(gòu)建Looper,但是不允許重復prepare,一個線程中只允許準備一個Looper
/**
* 通過當前調(diào)用線程獲得對應(yīng)的Looper
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
/**
* Looper開始工作
*/
public static void loop() {
final Looper me = myLooper();//獲得當前調(diào)用線程的Looper
if (me == null) {//必須先創(chuàng)建Looper
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//獲得Looper對應(yīng)的消息隊列
//...
for (;;) {//無限循環(huán)
Message msg = queue.next(); //當隊列中沒有數(shù)據(jù)或者下一條消息需要延時的時候可能會阻塞,next()內(nèi)部本身就是一個死循環(huán)
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//...
//成功取出下一條Message
try {
//將消息回調(diào)到Handler中處理
msg.target.dispatchMessage(msg);
} finally {
//...
}
//...
msg.recycleUnchecked();//當前消息分發(fā)處理完成,回收當前消息,用于后期復用
}
}
當一個新的線程準備好了Looper之后,要讓Looper開始工作需要loop(),在一個無限循環(huán)中不停地從Looper中的消息隊列中取出消息并且回調(diào)到對應(yīng)的Handler中處理,在這個過程中如果取不出消息則會阻塞,有消息可以獲得的時候會喚起。
Handler
/**
* 默認構(gòu)造函數(shù),會自動關(guān)聯(lián)當前調(diào)用線程的Looper
* 如果當前線程沒有創(chuàng)建Looper,會拋出異常
*/
public Handler() {
this(null, false);
}
/**
* 會自動關(guān)聯(lián)當前調(diào)用線程的Looper,并且指定消息的回調(diào)處理
* 如果當前線程沒有創(chuàng)建Looper,會拋出異常
*
* @param callback 用于處理消息的回調(diào),可以為null
*/
public Handler(Handler.Callback callback) {
this(callback, false);
}
/**
* 使用特定的Looper,并且指定消息的回調(diào)處理
*
* @param looper 必須為非空的Looper
* @param callback 用于處理消息的回調(diào),可以為null
*/
public Handler(Looper looper, Handler.Callback callback) {
this(looper, callback, false);
}
/**
* 使用特定的Looper
*
* @param looper 必須為非空的Looper
*/
public Handler(Looper looper) {
this(looper, null, false);
}
/**
* 會自動關(guān)聯(lián)當前調(diào)用線程的Looper,并且指定消息的回調(diào)處理
* 如果當前線程沒有創(chuàng)建Looper,會拋出異常
*
* @param callback 用于處理消息的回調(diào),可以為null
* @param async 在將消息推入消息隊列之前,會通過Message的setAsynchronous設(shè)置Message的屬性
*
* @hide
*/
public Handler(Handler.Callback callback, boolean async) {
mLooper = Looper.myLooper();//關(guān)聯(lián)當前調(diào)用線程的Looper
if (mLooper == null) {//要求當前線程必須初始化一個Looper
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//綁定Looper的消息隊列
mCallback = callback;
mAsynchronous = async;
}
/**
* 使用特定的Looper,并且指定消息的回調(diào)處理
*
*
* @param looper 必須為非空的Looper
* @param callback 用于處理消息的回調(diào),可以為null
* @param async 在將消息推入消息隊列之前,會通過Message的setAsynchronous設(shè)置Message的屬性,后續(xù)通過這個Handler發(fā)送的消息都是異步消息,如果消息隊列設(shè)置了同步屏障,那么只會執(zhí)行這些消息,否則就和普通消息沒有什么區(qū)別
*
* @hide
*/
public Handler(Looper looper, Handler.Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
這里有消息的異步屬性,不過后面會看到正常使用的時候并沒有什么作用。
重點是可以看到Handler在構(gòu)建的時候必須關(guān)聯(lián)指定的Looper,同時會綁定Looper里面對應(yīng)的MessageQueue,這樣關(guān)聯(lián)關(guān)系就構(gòu)建完成。
/**
* 發(fā)送一條延時處理的消息,并且指定回調(diào)
* @param r 當前消息的回調(diào)處理
* @param delayMillis 消息的處理延時
* @return 當前消息是否發(fā)送成功
*/
public final boolean postDelayed(Runnable r, long delayMillis)
{
//生成一個有指定回調(diào)的消息,并且在指定延時后發(fā)送消息
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
/**
* 獲得一條消息,并且指定回調(diào)
* @param r 當前消息的回調(diào)處理
* @return Message
*/
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();//嘗試復用一個Message
m.callback = r;//指定callback為對應(yīng)的Runnable
return m;
}
/**
* 發(fā)送一條延時處理的消息,并且指定回調(diào)
* @param msg 需要發(fā)送的消息
* @param delayMillis 消息的處理延時
* @return 當前消息是否發(fā)送成功
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {//處理延時必須大于等于0
delayMillis = 0;
}
//發(fā)送一條在當前時間+延時時間的時間點處理的消息
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* 將消息放入指定消息隊列當中
* @param msg 需要放入到消息隊列的消息
* @param uptimeMillis 消息需要被回調(diào)的時間
* @return true表示消息放入到消息隊列成功,false失敗
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//獲得當前Looper關(guān)聯(lián)的消息隊列
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//將消息放入當前Looper關(guān)聯(lián)的消息隊列
return enqueueMessage(queue, msg, uptimeMillis);
}
/**
* 將消息放入指定消息隊列當中
* @param queue 需要放入的消息隊列
* @param msg 需要放入到消息隊列中的消息
* @param uptimeMillis 當前消息回調(diào)的時間
* @return true表示消息放入到消息隊列成功,false失敗
*/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//指定當前消息的target為當前發(fā)送的Handler
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//調(diào)用消息隊列的方法將消息放入隊列當中
}
可以看到,Handler發(fā)送消息實際上就是把Message放入到對應(yīng)Looper的MessageQueue中,然后工作中的Looper會在指定的時間取出當前Message,然后再次回調(diào)到Handler當中。
/**
* 在Looper的loop執(zhí)行的線程中進行的回調(diào)
* 用于處理消息
* @param msg 需要處理的消息
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//如果消息是直接通過post(Runnable)的類似方式發(fā)送到隊列中的
handleCallback(msg);
} else {
if (mCallback != null) {//Handler在創(chuàng)建的時候如果指定了Callback,優(yōu)先回調(diào)到Callback中
if (mCallback.handleMessage(msg)) {//Callback處理消息完成是否結(jié)束本次回調(diào)
return;
}
}
//如果是直接通過sendMessage之類的方式發(fā)送到隊列中,并且在構(gòu)建Handler的時候沒有指定Callback的時候
//回調(diào)Handler中的方法
handleMessage(msg);
}
}
/**
* 回調(diào)消息在發(fā)送之前設(shè)置的Runnable
* @param message 當前需要處理的消息
*/
private static void handleCallback(Message message) {
message.callback.run();
}
/**
* 對于普通的sendMessage...之類的消息
* 可以通過直接復寫該方法從而進行統(tǒng)一的消息處理
* @param msg 需要處理的消息
*/
public void handleMessage(Message msg) {
}
當Looper成功獲得對應(yīng)Handler的消息的時候,會將消息在Looper所在的線程中交由Handler處理。優(yōu)先級如下:
1.Message有指定Runnable,回調(diào)Runnable,消息處理結(jié)束,否則2
2.Handler在構(gòu)建的時候指定Callback,回調(diào)Callback,如果Callback返回true,消息處理結(jié)束,否則繼續(xù)3
3.否則回調(diào)Handler中的handlerMessage方法,這個一般在開發(fā)中用的會多一些
除此之外可以看到,如果想要Handler的處理方法運行在非UI線程上,只需要指定非UI線程的Looper即可,比方說官方提供的HandlerThread和Handler組合的模式。
HandlerThread handlerThread = new HandlerThread("msg_handler");
handlerThread.start();
Handler.Callback callback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//在HandlerThread的線程中做些什么。。。
//在handleMessage之前處理
return false;
}
};
Handler handler = new Handler(handlerThread.getLooper(), callback){
@Override
public void handleMessage(Message msg) {
//在Callback執(zhí)行完畢之后,在HandlerThread的線程中繼續(xù)做些什么。。。
}
};
handler.sendEmptyMessage(0);
通過上面的這種方法就可以讓Handler在子線程中處理消息的回調(diào)。
Message
//復用池隊列數(shù)據(jù)變化鎖,類似讀寫鎖
private static final Object sPoolSync = new Object();
//當前復用池隊列的頭指針,或者說隊首消息
private static Message sPool;
//當前消息在消息鏈表中的下一條消息
Message next;
//當前復用池的大小
private static int sPoolSize = 0;
//復用池隊列的最大長度,超過不再接受舊的消息
private static final int MAX_POOL_SIZE = 50;
/**
* 從復用池隊列中嘗試復用已有的Message
* 如果沒有則新建
* @return 可用的Message
*/
public static Message obtain() {
synchronized (sPoolSync) {//復用池隊列數(shù)據(jù)變化鎖
if (sPool != null) {
Message m = sPool;//獲得隊首元素
//將隊首元素移出隊列
sPool = m.next;//當前隊列頭指針指向第二個元素
m.next = null;//斷開隊首元素指向第二個元素的指針,從而從隊列中移出完成
m.flags = 0; // clear in-use flag
sPoolSize--;//復用池隊列數(shù)量-1
return m;//復用當前復用池隊首的消息
}
}
//如果復用池沒有可以復用的消息,新建消息
return new Message();
}
/**
* 回收當前消息并且放在復用池鏈表的頭部
* 用于后期的復用
*/
void recycleUnchecked() {
//... 首先清空數(shù)據(jù)
synchronized (sPoolSync) {//復用池隊列數(shù)據(jù)變化鎖
if (sPoolSize < MAX_POOL_SIZE) {//當前復用池中的數(shù)據(jù)還沒到設(shè)置的最大值
//將當前消息插入到復用池的頭部
next = sPool;//當前消息指向復用池隊列的頭指針
sPool = this;//當前消息作為復用池隊列的頭指針,插入完成
sPoolSize++;//當前復用池中的消息數(shù)量+1
}
}
}
消息采用復用機制,通過一個后進先出的隊列來進行管理,隊列默認最大大小為50個Message。
在Android中Handler使用還是比較頻繁,通過Message的復用還是可以減少頻繁的內(nèi)存分配和回收。
Message在Looper中回調(diào)到Handler處理完成之后會自動回收,所以平時在使用的過程中不應(yīng)該手動進行回收。
MessageQueue
/**
* 將消息放入消息隊列當中
* 消息隊列會按照消息的執(zhí)行時刻進行排列,執(zhí)行時刻早的位于隊列的前面
* @param msg 需要放入的消息
* @param when 當前消息設(shè)置的執(zhí)行時刻
* @return true表示放入消息隊列成功,false在MessageQueue已經(jīng)quit的情況下會返回,表示當前MessageQueue不可用
*/
boolean enqueueMessage(Message msg, long when) {
//...一些檢查
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;//標記當前消息的執(zhí)行時刻
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//1.當前消息隊列為空
//2.當前消息需要立刻執(zhí)行
//3.當前消息執(zhí)行時刻比隊首消息還要早
//以上三種情況需要將當前消息插入到消息隊列的頭部即隊首
msg.next = p;
mMessages = msg;
needWake = mBlocked;//如果之前next處于阻塞狀態(tài),需要嘗試喚起隊列
} else {//在非隊列頭部的地方插入消息
//如果之前next處于阻塞狀態(tài),并且當前消息為異步消息,需要嘗試喚起隊列
needWake = 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()) {
needWake = false;
}
}
//將當前消息插入到消息隊列當中
msg.next = p;
prev.next = msg;
}
if (needWake) {//如果需要喚醒隊列,會嘗試喚起隊列,則next()會繼續(xù)執(zhí)行
nativeWake(mPtr);
}
}
return true;
}
/**
* 從消息隊列中獲取下一條用于回調(diào)的消息
* @return 用于回調(diào)的消息
*/
Message next() {
final long ptr = mPtr;
if (ptr == 0) {//當前消息隊列已退出
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {//開始
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);//阻塞指定的時間,nextPollTimeoutMillis指定下一次喚醒時間間隔
synchronized (this) {
final long now = SystemClock.uptimeMillis();//當前時刻
Message prevMsg = null;
Message msg = mMessages;//標記消息隊列隊首消息
//當target為null的時候,這種場景只能通過postSyncBarrier實現(xiàn)
if (msg != null && msg.target == null) {
//當通過postSyncBarrier放入的消息之后
//只會執(zhí)行指定了Asynchronous屬性的Handler發(fā)送的消息或者手動設(shè)置為Asynchronous的消息
//這種消息也就是相當于一個同步屏障的作用,導致MessageQueue只會取出異步消息
//從而導致Handler不會執(zhí)行同步的消息,而只會執(zhí)行異步的消息
//默認情況下這種消息不會自動移除,除非調(diào)用removeSyncBarrier
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {//當前消息隊列不為空
if (now < msg.when) {//當前還沒有到最快執(zhí)行的消息的執(zhí)行時刻
//延時指定的時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {//當前消息可以執(zhí)行
mBlocked = false;
if (prevMsg != null) {//當前消息為異步消息,此時可能是隊列中間的某一個消息
prevMsg.next = msg.next;//將當前消息前面的鏈表和后面的鏈表連在一起
} else {
mMessages = msg.next;//鏈表頭指針變成隊列中第二個消息
}
msg.next = null;//斷開當前消息在隊列中的指針,從而成功將當前消息從鏈表中移出
msg.markInUse();//標記的當前消息在使用中
return msg;//返回隊首消息
}
} else {
//消息隊列沒有滿足要求的消息或者消息隊列為空
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
//執(zhí)行到這里,說明當前消息隊列沒有可以執(zhí)行的消息,可能沒有到執(zhí)行時間或者隊列中就沒有消息
//接著嘗試進行當前消息隊列空閑的監(jiān)聽回調(diào),可以手動通過addIdleHandler添加監(jiān)聽回調(diào)
//空閑回調(diào)在一次next的過程中只能回調(diào)一次
//如果當前隊列為空或者還沒有到下一條消息的執(zhí)行時刻,即處于空閑狀態(tài)
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
//標記回調(diào)的數(shù)量,從而也意味著當前準備開始回調(diào)
//后續(xù)pendingIdleHandlerCount都不會小于0,也就不會再次回調(diào)
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {//當前沒有設(shè)置監(jiān)聽回調(diào)
mBlocked = true;//標記需要阻塞,因為當前隊列空閑
continue;//繼續(xù)循環(huán),從而進入到nativePollOnce進行阻塞
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new MessageQueue.IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 嘗試進行空閑回調(diào)
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final MessageQueue.IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {//如果空閑回調(diào)處理返回false,這里會自動移除回調(diào)
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 標記需要進行空閑回調(diào)的數(shù)量為0,這樣后續(xù)循環(huán)不會再次進行空閑回調(diào)
pendingIdleHandlerCount = 0;
//如果進行了空閑回調(diào),那么也許會有新消息的到來,則下一次進入循環(huán)先不阻塞
nextPollTimeoutMillis = 0;
}
}
MessageQueue中維持著一個按照消息執(zhí)行時刻排序的列表,插入消息的時候會按照時刻所處位置進行插入。
Looper在loop()中不停地循環(huán)MessageQueue的next()方法,next()方法會嘗試將第一條可用消息提供。
可以看到異步的消息會優(yōu)先執(zhí)行,通常使用下的消息都會有對應(yīng)的target,除非通過MessageQueue的postSyncBarrier()設(shè)置一個同步屏障,這樣會導致后續(xù)只執(zhí)行異步的消息。
空閑回調(diào)
1.如果消息隊列為空,首先會嘗試回調(diào)空閑Handler,然后再次嘗試獲取新消息,如果還沒有新消息,則會一直阻塞,等待enqueMessage中插入消息的喚起。
2.如果消息隊列不為空,但是第一條消息還沒有到指定的執(zhí)行時刻,首先會嘗試回調(diào)空閑Handler,然后再次嘗試獲取新消息,如果還沒有新消息,則會阻塞到第一條消息執(zhí)行的時刻。
3.每一次next()只能獲得一條消息,并且對應(yīng)的空閑回調(diào)只有一次。(使用例子可以看Glide對于activeResource弱內(nèi)存緩存的回收處理)
總結(jié)
如果把這個工作比作是一個工廠出品一個零器件。
Handler就是工廠大門兩端的加工人員,在大門入口負責把新來的零件放到循環(huán)工作的履帶,然后在大門出口負責最后把零件包裝一下然后送出去。
MessageQueue就是這個履帶,零件在履帶上面按照它們的執(zhí)行循序排列。
Looper就是履帶上面的自動機器手,在特定的時刻將履帶上面對應(yīng)的零件交給出口的Handler。