1.概述
Android中我們通常會把耗時操作放在子線程中评架,然后通過Handler來發(fā)送消息到主線程進行UI更新眷茁,本文通過探究源碼來分析Handler背后的原理是什么,本篇主要涉及Message纵诞、MessageQueue上祈、Looper、Handler這四個類的詳細(xì)分析挣磨。
2.Message
Message類是個final類不能被繼承雇逞,用作封裝數(shù)據(jù)的容器,是鏈表結(jié)構(gòu)茁裙,可以被發(fā)送給 Handler
- **主要屬性
public int what; //用戶定義消息代碼以便收件人可以識別這是哪一個Message
public int arg1; //如果只是想向message內(nèi)放一些整數(shù)值塘砸,可以使用arg1和arg2來代替setData方法
public int arg2;
public Object obj; //發(fā)送給接收器的任意對象,在使用 Messenger 跨進程傳遞消息時,通常使用它傳遞給接收者,在其他場景下我們一般使用 setData() 方法
/*package*/ static final int FLAG_IN_USE = 1 << 0; //標(biāo)識消息是否在被使用
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1; //標(biāo)識是否是異步消息
/*package*/ Bundle data; //setData()用到的Bundle
/*package*/ Handler target; // 與消息關(guān)聯(lián)的Handler
/*package*/ Runnable callback; //處理消息的回調(diào)
/*package*/ Message next; // 有時以鏈表的形式關(guān)聯(lián)后一個消息
private static final Object sPoolSync = new Object();
private static Message sPool; //消息池
private static int sPoolSize = 0;
從這些屬性中可以看出:
- Message.what 用來標(biāo)識干什么
- 可以存儲Bundle對象
- Message持有Handler的引用晤锥,將Handler引用賦值給target
- Message內(nèi)部有一個消息池
獲取Message
//構(gòu)造方法
public Message() {
}
Message的構(gòu)造方法是個空方法掉蔬,官方更推薦通過Message.obtain()來獲取一個Message,我們也經(jīng)常使用Handler.obtainMessage()來獲取消息矾瘾,該方法內(nèi)部也是調(diào)用Message.obtain()
//Handler 的obtainMessage()
public final Message obtainMessage(){
return Message.obtain(this);
}
Message.obtain()源碼如下:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // 清除在使用的標(biāo)識
sPoolSize--;
return m;
}
}
return new Message();
}
如果消息池中有消息的話就取出女轿,設(shè)置標(biāo)識未使用,消息的next屬性設(shè)為null壕翩,將消息池指向該消息的下一個蛉迹,如果沒有的話就調(diào)用構(gòu)造函數(shù)。Message.obtain()的其它重載方法都是在調(diào)用了該方法獲取到Message之后放妈,為其屬性賦值北救。例如下面這個重載方法:
public static Message obtain(Handler h, int what, int arg1, int arg2) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
return m;
}
既然消息是從消息池獲取的,那么消息池的消息從哪來的呢芜抒?是消息被回收時放入的珍策,消息回收時調(diào)用recycleUnchecked():
void recycleUnchecked() {
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) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
將消息的數(shù)據(jù)清除之后,這個消息加入了回收消息的鏈表中宅倒。
3.MessageQueue
MessageQueue管理著消息列表攘宙,消息由Handler來發(fā)送到MessageQueue,由Looper循環(huán)的取出
- 主要屬性
private final boolean mQuitAllowed;//表示MessageQueue是否允許退出
private long mPtr; //mPtr是native代碼相關(guān)的
Message mMessages; //表示消息隊列的頭Head
private boolean mBlocked; //next()調(diào)用是否被阻塞
MessageQueue持有消息列表的頭,是一個單鏈表的結(jié)構(gòu)
- 構(gòu)造方法
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed; //true允許退出
mPtr = nativeInit();
}
MessageQueue調(diào)用native方法來進行初始化蹭劈,該方法通常由Looper.prepare()調(diào)用
-
消息入隊:MessageQueue.enqueueMessage()
當(dāng)Handler發(fā)送消息到MessageQueue時疗绣,由該隊列的enqueueMessage()方法來負(fù)責(zé)把Message插入到隊列中
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) { // Message 必須關(guān)聯(lián)Handler
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) { //如果該Message 已經(jīng)在處理中,則拋出異常
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
Message p = mMessages; // 臨時變量p指向隊列頭
boolean needWake;
if (p == null || when == 0 || when < p.when) { // 插入的這個message應(yīng)該在第一個位置也就是隊首
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr); //激活消息隊列去獲取下一個消息
}
}
return true;
}
Message入隊時先判斷是否要插入隊首,如果不是的話則按照時間順序插入到某個合適的位置祟蚀。
- 消息出隊: MessageQueue.next()
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
... //省略代碼
nativePollOnce(ptr, nextPollTimeoutMillis); //等待被激活,然后從消息隊列中獲取消息
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages; //msg指向隊首
if (msg != null && msg.target == null) { // 隊首是消息屏障
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 下一個消息沒有到要處理的時機則設(shè)置激活等待時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取得一個消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
... //省略部分無關(guān)代碼
}
}
MessageQueue開啟一個循環(huán)來取消息割卖,隊列被激活之后前酿,首先判斷隊首是不是消息屏障,如果是則跳過所有的同步消息鹏溯,查找最先要處理的異步消息罢维。如果第一個待處理的消息還沒有到要處理的時機則設(shè)置激活等待時間;否則這個消息就是需要處理的消息丙挽,將該消息設(shè)置為 inuse肺孵,并將隊列設(shè)置為非 blocked 狀態(tài),然后返回該消息颜阐。
4.Looper
用于為Thread運行消息循環(huán)的類平窘,Thread默認(rèn)沒有消息循環(huán)的Looper,需要在Thread中調(diào)用Looper.prepare()來創(chuàng)建一個Looper凳怨,然后調(diào)用Looper.loop()來執(zhí)行消息循環(huán)瑰艘。
- 主要屬性
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // 主線程中的Looper
final MessageQueue mQueue; // 關(guān)聯(lián)的消息隊列
final Thread mThread; //Looper所在的線程
從屬性中可以看到Looper中持有一個消息隊列,就可以調(diào)用MessageQueue的相關(guān)方法肤舞。
- 構(gòu)造函數(shù)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
構(gòu)造函數(shù)就兩行代碼紫新,新建了一個MessageQueue,把Looper所在當(dāng)前線程賦值給mThread屬性李剖,但是這個方法是private芒率,外部不能通過調(diào)用它來構(gòu)造一個Looper,該方法在Looper.prepare中被調(diào)用:
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));
}
prepare()方法通過構(gòu)造方法生一個Looper然后把它保存在了ThreadLocal中,不過在這之前先判斷了ThreadLoacl中是否保存了一個Looper篙顺,如果有的話就會拋出異常偶芍,這說明一個Thread只會有一個Looper與之關(guān)聯(lián),那么來看一下ThreadLocal是什么
-
ThreadLocal
ThreadLocal的作用是提供線程內(nèi)的局部變量慰安,這種變量在線程的生命周期內(nèi)起作用腋寨,在本線程內(nèi)隨時隨地可取,隔離其他線程化焕。所以Looper通過ThreadLocal可以在線程中存取萄窜,線程取得與之關(guān)聯(lián)的Looper之后,就可以調(diào)用Looper.loop()循環(huán)取出消息,并且每個線程的Looper是獨立的查刻,不相關(guān)的键兜。 - Looper.loop() 循環(huán)取出消息并處理
public static void loop() {
final Looper me = myLooper();
if (me == null) { //當(dāng)前線程必須創(chuàng)建 Looper 才可以執(zhí)行
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
//底層對 IPC 標(biāo)識的處理,不用關(guān)心
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { //無限循環(huán)模式
Message msg = queue.next(); //從消息隊列中讀取消息穗泵,可能會阻塞
if (msg == null) { //當(dāng)消息隊列中沒有消息時就會返回普气,不過這只發(fā)生在 queue 退出的時候
return;
}
//...
try {
msg.target.dispatchMessage(msg); //調(diào)用消息關(guān)聯(lián)的 Handler 處理消息
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
//...
msg.recycleUnchecked(); //標(biāo)記這個消息被回收
}
}
loop()方法首先取到Looper關(guān)聯(lián)的MessageQueue,開啟一個無限循環(huán)調(diào)用MessageQueue.next()來不斷的取出消息佃延,取出消息之后調(diào)用msg.target.dispatchMessage(msg);前面介紹Message時說到Message的target屬性就是Handler现诀,這里就將消息交給了Handler來處理。要注意將消息交給Handler處理是在Loop()中履肃,所以Looper.loop()被調(diào)用的線程就是Handler處理消息的線程仔沿。例如通過Handler更新UI時,在子線程調(diào)用Handler.sendMessage()發(fā)送消息尺棋,由于UI線程的Looper.loop()已經(jīng)由系統(tǒng)在UI線程調(diào)用封锉,所以Handler處理消息也在主線程,總結(jié)來說:Looper.loop()在哪個線程調(diào)用的膘螟,Handler就切換到哪個線程處理消息成福。
5.Handler
Handler允許你發(fā)送和處理一個Message和Runnable對象,每個Handler實例都和一個單獨的線程和線程的MessageQueue相關(guān)聯(lián)荆残。
- 主要屬性
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
Callback 是Handler內(nèi)部的一個接口奴艾,它可以作為構(gòu)造函數(shù)的參數(shù)用于新建 Handler。
public interface Callback {
public boolean handleMessage(Message msg);
}
//Handler的一個構(gòu)造函數(shù)
public Handler(Callback callback) {
this(callback, false);
}
// 例如:
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//這里處理消息
return false;
}
});
Handler有許多重載的構(gòu)造函數(shù)脊阴,他們的作用都是給上面的這幾個屬性賦值握侧,要注意的是mLooper在賦值時由兩種方式:
1.構(gòu)造Handler時不傳遞Looper對象,Looper從當(dāng)前線程獲取嘿期,當(dāng)前線程不是UI線程時品擎,需要先調(diào)用Looper.prepare()
2..構(gòu)造Handler時構(gòu)造參數(shù)傳遞Looper對象,Handler就與傳遞的Looper和Looper所在的線程相關(guān)聯(lián)
MessageQueue 由Looper.mQueue倆獲取备徐,所以構(gòu)造Handler時就已經(jīng)與所在線程萄传、Looper、MessageQueue向關(guān)聯(lián)了蜜猾。
-
Handler發(fā)送消息原理
我們用Handler發(fā)送消息時通常使用 handler.sendMessage(message)秀菱,下面我們來看這個方法及其類似方法是怎么實現(xiàn)的:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what) {
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
這幾個發(fā)送Message的方法最后都調(diào)用了sendMessageAtTime()方法,這個方法源碼如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
參數(shù)msg即是我們要發(fā)送的Message蹭睡,uptimeMillis參數(shù)則表示發(fā)送消息的時間衍菱,它的值等于自系統(tǒng)開機到當(dāng)前時間的毫秒數(shù)再加上延遲時間,方法中的mQueue是Handler初始化的時候賦值的肩豁,然后把這三個參數(shù)傳入enqueueMessage()方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
把Message的target設(shè)置為當(dāng)前Handler脊串,最后queue.enqueueMessage(msg, uptimeMillis)把消息插入消息隊列中辫呻,至此消息的發(fā)送就完成了
-
Handler接受消息并處理
既然Message進入了消息隊列,就有出隊的方法琼锋,出隊操作是由Looper.loop()來執(zhí)行的放闺,loop()方法開啟一個無限循環(huán)了從MessageQueue去取出Message,然后調(diào)用Message.target.dispatchMessage(),Message.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);
}
}
可以看到怖侦,Handler 在處理消息時會進行判斷:
- msg.callback 不為空
如果Message的callback屬性不為空的話就直接調(diào)用Runnable的run()方法。 - mCallback 不為空
如果使用Callback構(gòu)造了Handler谜叹,則會進入到Callback接口的handleMessage(),該方法返回true的話就不會往下走了匾寝。 - 最后就調(diào)用 Handler.handleMessage() 方法,例如:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//處理Message
}
};
至此Handler就可以獲取到Message并處理了
- Handler.post()系列方法
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
post()系列方法和上面的sendMessage()系列方法一樣荷腊,最終都是調(diào)用sendMessageAtTime(),不同的地方在于參數(shù)傳了一個getPostMessage(r)旗吁,我們來看一下這個方法
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
該方法就是構(gòu)造了一個Message,并把參數(shù)Runnable設(shè)置為Message的callback屬性停局,sendMessageAtTime()接下里就和上面的流程一樣了
總結(jié)
- Handler的構(gòu)造時會關(guān)聯(lián)一個線程,并發(fā)送消息到該線程關(guān)聯(lián)的MessageQueue中
- Thread默認(rèn)沒有Looper香府,需要調(diào)用Looper.prepare()來初始化Looper董栽,且只能初始化一次
- 每個looper會維護一個MessageQueue,Looper負(fù)責(zé)循環(huán)的從MessageQueue取消息并交給Handler處理
Handler異步處理消息流程如下: