1 前言
Handler在Android開發(fā)中使用的比較多,通常Handler是在主線程中創(chuàng)建,子線程拿到這個(gè)Handler向主線程中發(fā)送消息。
那么如果需要主線程中向子線程中發(fā)送消息呢?
本文只是提出這個(gè)問題,并不在此提供實(shí)現(xiàn)這種場(chǎng)景的例子,寫這篇文章的目的主要是理解Handler的運(yùn)行原理,以便更好、更靈活的運(yùn)用Handler。
2 Android消息機(jī)制簡介
- Android的消息機(jī)制主要是指Handler的運(yùn)行機(jī)制,Handler的運(yùn)行依賴于MessageQueue和Looper,當(dāng)然,既然是消息機(jī)制,通常也需要用到Message。
- Handler、Looper、MessageQueue和Message的工作原理就像是生產(chǎn)線,Looper是發(fā)動(dòng)機(jī),MessageQueue就是傳送帶,Handler是工人,Message就是待處理的產(chǎn)品
3 Handler的工作流程
一個(gè)應(yīng)用的啟動(dòng)需要有main方法作為啟動(dòng)的入口,在Android中這個(gè)main方法在ActivityThread類中,查看這個(gè)類的main方法可以看到:
public static final void main(String[] args) {
//省略無關(guān)代碼
...
//主線程中調(diào)用Looper.prepareMainLooper()方法創(chuàng)建Looper
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
//主線程中調(diào)用Looper.loop(),開始輪詢,取消息
Looper.loop();
if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}
...
}
}
3.1 Looper輪詢器
在ActivityThread的main方法中首先調(diào)用Looper.prepareMainLooper()方法,我們來看一下,這個(gè)方法中做了哪些工作:
在Looper類中
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
if (Process.supportsProcesses()) {
myLooper().mQueue.mQuitAllowed = false;
}
}
該方法中首先調(diào)用本類中的prepare()方法,這個(gè)方法的主要作用是創(chuàng)建Looper對(duì)象,并且把該對(duì)象綁定到當(dāng)前線程中,在這里既然是主線程調(diào)用的,那么該Looper對(duì)象也就是在主線程當(dāng)中
在Looper類中
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//把Looper綁定到當(dāng)前線程
sThreadLocal.set(new Looper());
}
在Looper的構(gòu)造方法中,我們可以看到:
private Looper() {
//創(chuàng)建消息隊(duì)列
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
首先,Looper這個(gè)類的構(gòu)造方法是私有的,也就是說不允許外界直接new出來Looper對(duì)象。在創(chuàng)建Looper的時(shí)候,同時(shí)創(chuàng)建了一個(gè)消息隊(duì)列MessageQueue。
所以請(qǐng)注意,消息隊(duì)列MessageQueue是在Looper創(chuàng)建的時(shí)候,同時(shí)創(chuàng)建的
通過調(diào)用Looper.loop()方法,開始輪詢消息:
public static final void loop() {
Looper me = myLooper();
//通過拿到Looper對(duì)象,取獲取與之對(duì)應(yīng)的消息隊(duì)列MessageQueue
MessageQueue queue = me.mQueue;
...
//通過死循環(huán)方式去取消息
while (true) {
//調(diào)用MessageQueue的next()方法取消息,這個(gè)過程也是死循環(huán)
Message msg = queue.next(); // might block
if (msg != null) {
...
//取到消息之后,交給發(fā)送該消息的Handler取處理消息
msg.target.dispatchMessage(msg);
...
//回收消息,在Message中維護(hù)的有一個(gè)消息池
msg.recycle();
}
}
}
通過上述簡單的敘述,我們可以明白,在Activity啟動(dòng)的時(shí)候創(chuàng)建的一個(gè)與主線程相關(guān)的Looper和對(duì)應(yīng)的消息隊(duì)列MessageQueue,并且通過消息隊(duì)列通過阻塞的方式去取消息。
3.2 Handler的工作過程
Handler的工作過程主要包含發(fā)送消息和處理消息。Handler的構(gòu)造方法有多個(gè),他們的共同點(diǎn)是均會(huì)獲取當(dāng)前線程的Looper對(duì)象和消息隊(duì)列MessageQueue對(duì)象
//Handler的構(gòu)造方法1
public Handler() {
...
//獲取當(dāng)前線程的Looper對(duì)象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//獲取消息隊(duì)列對(duì)象
mQueue = mLooper.mQueue;
mCallback = null;
}
//Handler的構(gòu)造方法2
public Handler(Callback callback) {
...
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;
}
//Handler的構(gòu)造方法3
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
//Handler的構(gòu)造方法4
public Handler(Looper looper, Callback callback) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
}
發(fā)送消息可以通過一系列send方法,也可以通過一系列post方法,不過post方法最終還是通過send方法去實(shí)現(xiàn)。
用send方法,最終也會(huì)調(diào)用一個(gè)方法如下:
//在Handler中
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
//調(diào)用msg.target的方法把Message和發(fā)送它的Handler綁定,所以Looper能夠把正確的Message交給發(fā)送它的Handler處理
msg.target = this;
//調(diào)用MessageQueue的enqueueMessage方法把消息加入消息隊(duì)列
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
可以看到,Handler發(fā)送消息的過程就是向消息隊(duì)列中插入一條消息。前面已經(jīng)講到,MessageQueue調(diào)用next方法去輪詢消息,那么當(dāng)MessageQueue拿到消息之后,把消息傳遞給Looper,最終交給Handler去處理,即dispatchMessage方法會(huì)被調(diào)用。此時(shí),Handler開始處理消息。值得一提的是,在消息隊(duì)列中可能有不同Handler發(fā)送的多個(gè)消息,通過在發(fā)送消息的時(shí)候把Message和發(fā)送它的Handler綁定,Looper就會(huì)把消息正確的交給發(fā)送它的Handler來處理。dispatchMessage方法如下:
//Handler中
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Handler處理消息的過程如下:
-
查看Message的callback是否為null,不為null的話就通過handleCallback(msg)方法處理消息。這里的callback實(shí)際上就是一個(gè)Runnable對(duì)象,如果以post方式去發(fā)送消息,最終就會(huì)調(diào)用handleCallback(msg)方法去處理,這個(gè)方法內(nèi)容為:
private final void handleCallback(Message message) { message.callback.run(); }
檢查mCallBack是否為null,如果不為null就調(diào)用mCallBack的handleMessage(msg)方法。這個(gè)mCallBack是CallBack接口的子類對(duì)象,前面已經(jīng)說過Handler的構(gòu)造方法中有兩個(gè)可以用到這個(gè)CallBack。
如果mCallBack為null,最終就會(huì)調(diào)用Handler的handleMessage(msg)方法,這個(gè)方法通常是在創(chuàng)建Handler時(shí)被使用者重寫的方法。
需要說明的是:在主線程當(dāng)前我們創(chuàng)建Handler時(shí)并沒有自己創(chuàng)建Looper對(duì)象,這是因?yàn)橹骶€程已經(jīng)為我們創(chuàng)建好了;如果要在子線程當(dāng)前創(chuàng)建Handler,一定要在之前創(chuàng)建Looper對(duì)象,即調(diào)用Looper.prepare()方法。
3.3 MessageQueue消息隊(duì)列
前面的內(nèi)容已經(jīng)講了很多關(guān)于MessageQueue的東西,這里也就不再贅述,MessageQueue主要包含兩個(gè)操作:插入消息(enqueueMessage方法)和讀取消息(next方法)。讀取的過程也伴隨著刪除操作。MessageQueue的的內(nèi)部實(shí)際上是通過一個(gè)單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護(hù)消息列表,這主要也是因?yàn)閱捂湵碓诓迦牒蛣h除上比較有優(yōu)勢(shì)。
final boolean enqueueMessage(Message msg, long when) {
...
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// 當(dāng)前發(fā)送的message需要馬上被處理調(diào),needWake喚醒狀態(tài)置true
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
// 當(dāng)前發(fā)送的message被排隊(duì)到其他message的后面,needWake喚醒狀態(tài)置為false
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
// 是否喚醒主線程
if (needWake) {
nativeWake(mPtr);
}
return true;
}
enqueueMessage的主要操作其實(shí)就是單鏈表的插入操作,根據(jù)時(shí)間看當(dāng)前發(fā)送的Message是否需要馬上處理。這個(gè)enqueueMessage方法是Handler發(fā)送消息的時(shí)候調(diào)用。
下面來看next方法:
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) {
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
}
} else {
nextPollTimeoutMillis = -1;
}
...
}
可以看到,next方法返回一個(gè)Message對(duì)象。next方法中也是采用阻塞的方式去獲取消息隊(duì)列中的消息,一旦有消息立即返回并且將它從單鏈表中移除。如果沒有消息就一直阻塞。前面已經(jīng)提到,這個(gè)取消息的next方法是在Looper的loop()方法中調(diào)用。
3.4 Message消息載體
Message只有一個(gè)無參構(gòu)造方法,但是Message有多個(gè)obtain靜態(tài)方法來返回Message對(duì)象。
采用哪種方式創(chuàng)建Message對(duì)象都可以,但是建議采用obtain方法來創(chuàng)建。這是因?yàn)镸essage通過在內(nèi)部構(gòu)建一個(gè)鏈表來維護(hù)一個(gè)被回收的Message對(duì)象池。當(dāng)用戶調(diào)用obtain方法時(shí)會(huì)優(yōu)先從池中獲取,如果池中沒有則創(chuàng)建新的Message對(duì)象。同時(shí)在使用完畢之后,進(jìn)入池中以便于復(fù)用。這個(gè)在Looper.loop()方法可以看到一點(diǎn)端倪,在使用完畢時(shí)候調(diào)用了Message的recycle()方法。
下面是obtain方法創(chuàng)建Message對(duì)象的流程:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
4 小結(jié)
- 在應(yīng)用啟動(dòng)時(shí),會(huì)開啟一個(gè)主線程(即UI線程),會(huì)創(chuàng)建一個(gè)Looper對(duì)象,并把該對(duì)象綁定到主線程中。Looper對(duì)象被封裝在ThreadLocal中,使得不同線程間的Looper不能共享
- 創(chuàng)建Looper時(shí)同時(shí)創(chuàng)建了MessageQueue消息隊(duì)列對(duì)象
- Handler通過send方法或者post方法,把消息加入消息隊(duì)列MessageQueue中
- 主線程中調(diào)用Looper的loop()方法,會(huì)開啟消息循環(huán),不斷的從消息隊(duì)列中取出消息
- Looper拿到消息之后調(diào)用Handler的dispatchMessage方法來處理消息