系列文章:
前言
Android和Windows一樣都采用了消息機(jī)制,從開發(fā)角度說,Handler是Android消息機(jī)制的上層接口,開發(fā)過程中和Handler交互即可。Android規(guī)范限制我們不能在子線程中更新UI,只能在主線程中更新UI,Handler可以輕松的將一個任務(wù)切換到Handler所在的線程中去執(zhí)行。所以,開發(fā)過程中Handler通常被用來更新UI,利用Handler將更新UI的操作切換到主線程中執(zhí)行。
ViewRootImpl對我們的UI操作進(jìn)行了驗(yàn)證,具體是在checkThread()方法中完成的,如下:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
ViewRootImpl是所有View的根,Window和View通過ViewRootImpl建立聯(lián)系,checkThread()方法校驗(yàn)當(dāng)前線程是否是ViewRootImpl被創(chuàng)建時所在的線程。具體請參考Android中子線程真的不能更新UI嗎?
說明:本文中的源碼都是基于Android-25版本。
Handler
Android的消息機(jī)制主要是指Handler的運(yùn)行機(jī)制,Handler的運(yùn)行需要MessageQueue和Looper支撐,MessageQueue是消息隊(duì)列,內(nèi)部存儲了一組消息,而Looper則是消息循環(huán),Looper會無限循環(huán)查看是否有新消息,如果有的話處理消息,沒有的話就一直等待。
Looper中有一個特殊概念ThreadLocal,ThreadLocal并不是線程,作用是在每個線程中存儲數(shù)據(jù),可以讓不同線程保存在同一個ThreadLocal中的對象數(shù)據(jù)互不干擾。ThreadLocal的工作原理請參考:Android的消息機(jī)制之ThreadLocal的工作原理
工作過程
Handler在創(chuàng)建過程時會采用當(dāng)前線程的Looper來構(gòu)造消息循環(huán)系統(tǒng),那么Handler內(nèi)部是如何獲取到Looper呢?通過ThreadLocal可以很輕松的獲取每個線程的Looper,但是線程默認(rèn)是沒有Looper的,要使用Handler就必須為線程創(chuàng)建Looper。而主線程(UI)線程即ActivityThread被創(chuàng)建時就初始化了Looper,所以我們在主線程中可以默認(rèn)使用Looper。
Handler的默認(rèn)構(gòu)造方法最終會通過以下構(gòu)造方法實(shí)現(xiàn):
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;
}
從中可以看到,如果當(dāng)前線程沒有Looper,則會拋出異常,而mLooper是由Looper.myLooper()方法返回的,代碼如下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
Looper.myLooper()就是返回了保存在ThreadLocal中的Looper對象。
Handler創(chuàng)建完畢后,Handler和Looper以及MessageQueue便協(xié)同工作,構(gòu)成一個消息系統(tǒng)。通過Handler的post系列方法將一個Runnable投遞到Looper,或者send系列方法發(fā)送一個消息到Looper中,通過調(diào)用MessageQueue的enqueueMessage()方法將消息放入消息隊(duì)列中,此時Looper發(fā)現(xiàn)有新消息到來,處理此消息。最終消息中的Runnable或者Handler的handleMessage()方法就會被調(diào)用,這樣就又將任務(wù)切換到創(chuàng)建Handler的線程中去了。過程如圖所示:

工作原理
Handler的工作主要包括發(fā)送和接收消息。
發(fā)送消息
上面提到,發(fā)送消息是通過一系列post或者send方法實(shí)現(xiàn),post系列的方法最終都是通過send系列方法實(shí)現(xiàn)的。Handler中有關(guān)發(fā)送消息的send系列方法源碼如下:
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 sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到除了sendMessageAtFrontOfQueue()方法,其余最終都是調(diào)用sendMessageAtTime()方法,在sendMessageAtTime()方法中調(diào)用enqueueMessage()方法,再調(diào)用MessageQueue的enqueueMessage()方法,發(fā)送消息就是向消息隊(duì)列中插入一條消息,MessageQueue的next()方法將這條消息返回給Looper。
處理消息
Looper接收到消息后處理,并最終交由Handler的dispatchMessage()處理,源碼如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先檢查Message的callback,不為null則調(diào)用handleCallback(),源碼如下:
private static void handleCallback(Message message) {
message.callback.run();
}
Message的callback是一個Runnable對象,就是Handler中post方法的Runnable參數(shù)。post方法源碼如下:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
post方法利用sendMessageDelayed()方法發(fā)送消息,其中又調(diào)用了getPostMessage()方法,其源碼如下:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
該方法傳入一個Runnable參數(shù),得到一個Message對象,將Runnable參數(shù)賦值給Message對象的callback字段。
然后檢查mCallback,不為null則調(diào)用mCallback的handleMessage(),Callback是一個接口,定義如下:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
使用Callback創(chuàng)建Handler不需要派生一個Handler子類,通常開發(fā)過程中都是派生Handler子類并重寫其handleMessage()方法來處理具體消息,Callback提供了另一種使用Handler方法。
最后不符合以上兩點(diǎn)時,就調(diào)用Handler的handleMessage()來處理消息。
Looper
Looper在Android消息機(jī)制中扮演消息循環(huán)的角色,它會不斷從MessageQueue中查看是否有新消息,如果有新消息就立即處理,否則一直阻塞在那里。
創(chuàng)建Looper
首先看一下構(gòu)造方法,在構(gòu)造方法中它會創(chuàng)建一個MessageQueue消息隊(duì)列,然后將當(dāng)前線程的對象保存起來。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Handler的工作需要Looper,沒有Looper的線程就會報錯,那么如何為線程創(chuàng)建Looper呢?通過Looper.prepare()即可為當(dāng)前線程創(chuàng)建一個Looper,同時會將Looper保存在ThreadLocal中,Handler的構(gòu)造函數(shù)便是從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));
}
接著通過Looper.loop()來開啟消息循環(huán)。為一個子線程創(chuàng)建Looper和Handler的代碼如下所示:
new Thread("Thread#2"){
@Override
public void run(){
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
};
}.start();
Looper除了prepare()方法外,還提供了prepareMainLooper()方法,這個方法主要是給主線程即ActivityThread創(chuàng)建Looper使用的,本質(zhì)也是通過prepare()方法來實(shí)現(xiàn)的,源碼如下:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
在程序啟動的時候,系統(tǒng)已經(jīng)幫我們自動調(diào)用了Looper.prepare()方法。ActivityThread中的main()方法調(diào)用了Looper.prepareMainLooper()方法,而這個方法又會再去調(diào)用Looper.prepare()方法。因此我們應(yīng)用程序的主線程中會始終存在一個Looper對象,從而不需要再手動去調(diào)用Looper.prepare()方法了。
由于主線程Looper比較特殊,所以Looper提供了一個getMainLooper()方法,通過它可以在任何地方獲取主線程的Looper。
Looper退出
Looper也提供了退出方法,quit()和quitSafely(),區(qū)別在于quit()是直接退出Looper,而quitSafely()只是設(shè)定一個退出標(biāo)記,然后把消息隊(duì)列中的已有消息全部處理完畢后再退出。quit()方法最終調(diào)用的是MessageQueue中的removeAllMessagesLocked()方法,quitSafely()最終調(diào)用的是MessageQueue中的removeAllFutureMessagesLocked()方法,源碼如下:
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
}
else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
Looper退出后,通過Handler發(fā)送的消息會失敗,Handler的send方法會返回false,在子線程中如果為其手動創(chuàng)建了Looper,消息處理完畢后應(yīng)該調(diào)用quit()方法來終止消息循環(huán),否則這個子線程會一直處于等待狀態(tài),退出Looper以后,這個子線程就會立刻終止,因此建議不需要的時候終止Looper。
消息循環(huán)
Looper通過loop()方法開啟消息循環(huán),源碼實(shí)現(xiàn)如下:
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();
for (;;) {
Message msg = queue.next(); // might block
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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.recycleUnchecked();
}
}
loop()方法是一個死循環(huán),唯一跳出循環(huán)的方法是MessageQueue的next()方法返回了null。當(dāng)Looper的退出方法被調(diào)用時,通知消息隊(duì)列消息退出,當(dāng)消息隊(duì)列被標(biāo)記為退出狀態(tài)時,它的next()方法就會返回null。也就是說Looper必須退出,否則loop()方法會無限循環(huán)下去。
loop()方法會調(diào)用MessageQueue中的next()方法來獲取新消息,而next()是一個阻塞操作,沒有新消息時會一直阻塞在那里,同理導(dǎo)致loop()阻塞,如果MessageQueue的next()方法返回了新消息,Looper便會處理這條消息,通過以下語句執(zhí)行:
msg.target.dispatchMessage(msg);
這里的msg.target是發(fā)送這條消息的Handler對象,這樣通過Handler的dispatchMessage()方法來處理消息,dispatchMessage()是在創(chuàng)建Handler時所使用的Looper中執(zhí)行的,這樣就將代碼邏輯切換到指定的線程中了。
Message & MessageQueue
Message在線程之間傳遞消息,Message有what字段,是消息類型字段,arg1和arg2攜帶一些整型數(shù)據(jù),obj字段攜帶一個object對象。
MessageQueue是消息隊(duì)列,存放所有通過Handler發(fā)送的消息。消息一直存放在消息隊(duì)列中,等待被處理。每個線程只會有一個MessageQueue對象。
MessageQueue主要包含兩個操作,插入和讀取,讀取伴隨著刪除操作。euqueueMessage()的作用是往消息隊(duì)列中插入一條消息,next()的作用是從消息隊(duì)列中讀取一條消息并移除。盡管MessageQueue叫做消息隊(duì)列,但是其內(nèi)部是通過單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護(hù)消息列表。euqueueMessage()的源碼如下:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
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;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
從源碼中可以分析出,當(dāng)前消息隊(duì)列的頭結(jié)點(diǎn)為空或待插入的消息需要被立即執(zhí)行時,就讓當(dāng)前消息成為消息隊(duì)列的新的頭結(jié)點(diǎn),并且如果消息隊(duì)列處于阻塞狀態(tài),則將消息隊(duì)列喚醒;否則則按消息等待被執(zhí)行的時間順序,將待插入消息插入消息隊(duì)列中,最后如果需要喚醒消息隊(duì)列,則通過native方法nativeWake()來喚醒消息隊(duì)列。
下面再看看next()方法的源碼:
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
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);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
}
...
}
}
可以發(fā)現(xiàn)next()方法是一個無限循環(huán)方法,如果消息隊(duì)列中沒有消息,其會一直阻塞在這里,有新消息到來時,next()方法會返回這條消息并將其從單鏈表中移除。
Android中為什么主線程不會因?yàn)長ooper.loop()里的死循環(huán)卡死?
問題描述
Android程序的入口點(diǎn)可以認(rèn)為是ActivityThread類的main()方法,源碼如下:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
...
}
可以看到Looper開啟了消息循環(huán),loop()方法是一個死循環(huán),但是并沒有看見有相關(guān)代碼為這個死循環(huán)準(zhǔn)備了一個新線程去運(yùn)轉(zhuǎn),但是主線程卻并不會因?yàn)?code>Looper.loop()中的這個死循環(huán)卡死,這是為什么呢?
原因
從上述代碼我們可以發(fā)現(xiàn),首先調(diào)用prepareMainLooper()方法為主線程創(chuàng)建一個消息隊(duì)列;其次,生成一個ActivityThread對象,在其初始化代碼中會創(chuàng)建一個H(Handler)對象,即ActivityThread.H,它內(nèi)部定義了一組消息類型,主要包含了四大組件的啟動和停止過程,如下所示:
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
...
}
thread.attach(false)生成了一個AppplicationThread(Binder)對象,ActivityThread通過ApplicationThread和AMS進(jìn)行進(jìn)程間通信,AMS以進(jìn)程間通信方式完成ActivityThread的請求后會回調(diào)ApplicationThread中的Binder方法,Binder負(fù)責(zé)接遠(yuǎn)程ActivityManagerService(AMS)的IPC調(diào)用,用于接收系統(tǒng)服務(wù)AMS發(fā)來的消息,收到消息后,通過Handler將消息發(fā)送到消息隊(duì)列,UI主線程會異步的從消息隊(duì)列中取出消息并執(zhí)行操作;最后,UI主線程調(diào)用Looper.loop()進(jìn)入消息循環(huán)。
Android的Handler消息機(jī)制涉及到Linux的pipe/epoll機(jī)制,MessageQueue沒有消息時,阻塞在那里,主線程會釋放CPU進(jìn)入休眠狀態(tài),通過Linux系統(tǒng)的epoll機(jī)制中的epoll_wait函數(shù)進(jìn)行等待,當(dāng)有新消息來臨時,往pipe(管道)寫入端寫入消息來喚醒主線程,其實(shí)就是一個生產(chǎn)消費(fèi)模型。
(還有一個疑問,那就是怎么響應(yīng)點(diǎn)擊事件呢?或者說對通常的GUI模型,如windows都是怎么實(shí)現(xiàn)的呢?生產(chǎn)消費(fèi)模型?)
以上部分摘自柯元旦<Android內(nèi)核剖析>
Handler的其它用法
除了通過編寫子線程并結(jié)合Handler發(fā)送消息改變UI外,Handler還有一些其他用法。
View中的post()方法
代碼如下所示:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
發(fā)現(xiàn)View的post()方法就是調(diào)用了Handler中的post()方法,前文已經(jīng)說過Handler的post()方法了,不再多解釋。
Activity中的runOnUiThread()方法
代碼如下所示:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
先判斷當(dāng)前線程是否是UI線程,如果不是則調(diào)用Handler的post()方法,否則就直接調(diào)用Runnable對象的run()方法。
參考信息
- 任玉剛.《Android開發(fā)藝術(shù)探索》
- Android中為什么主線程不會因?yàn)長ooper.loop()里的死循環(huán)卡死?