(注:本文源碼基于Android7.1)
如果讀者做過Android開發(fā),相信對Android中Handler這個類并不陌生规肴。通常情況下,Handler是線程與UI線程交互的工具晴裹。本文希望通過Android的源代碼,讓大家能更加深刻地體會到Handler相關(guān)類的實現(xiàn)原理和內(nèi)部機制。
我們知道,通過Handler發(fā)送消息,大致有兩種方式:
1.Handler.post(Runnable)
2.Handler.sendMessage(Message)
其實,這兩種方式是一種方式,因為我們通過postRunnable最終也會轉(zhuǎn)化為sendMessage。
//code android/os/Handler.java
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r)/*將Runnable類型轉(zhuǎn)化為Message類型*/, 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
通過上面的Handler的源碼,我們可以看出,不論上層的接口是怎么樣的,最終的Handler所拋出的類型永遠(yuǎn)是Message。而當(dāng)我們通過Handler.send方法發(fā)送一個Message的時候,最終都會調(diào)用到Handler的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);
}
這里,引出我們的一個新的主角:MessageQueue,通過類名,我們也能見名知意,就是一個消息隊列无蜂。消息隊列是一種生產(chǎn)消費的設(shè)計模式,Handler作為生產(chǎn)者通過enqueueMessage方法往queue中入隊消息紊服。那么,這個MessageQueue又是如何生成的呢檀轨?
public Handler(Callback callback, boolean async) {
....
mLooper = Looper.myLooper();
....
mQueue = mLooper.mQueue;//獲取一個Queue
....
}
我們通過Handler的構(gòu)造器可以看出,Handler中的MessageQueue對象,實際上是來源于Looper對象。那么又帶來另外一個問題,Looper對象又是從哪里來的呢欺嗤?我們這篇文章開始沒多久,沒想到各大主角就集聚一堂参萄。從上面的代碼中,我們可以看出,Handler對象中的Looper對象源于Looper.myLooper()方法。
//code Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
myLooper方法中,將通過一個全局靜態(tài)量sThreadLocal來獲取當(dāng)前線程中唯一的Looper,而這個Looper是當(dāng)前線程調(diào)用Looper.prepare方法生成并放入sThreadLocal中的煎饼。
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));
}
那么又有另外一個問題,UI線程中,我們并沒有調(diào)用Looper.prepare方法,那么UI線程中的Looper又是在哪兒生成的呢讹挎?
實際上,要回答合格問題,需要從UI線程的生成開始說起:UI線程伴隨著Android進程的啟動而啟動,入口在ActivityThread.main中。而ActivityThread的main方法中,將通過Looper.prepareMainLooper()方法來生成UI線程的Looper:
//code ActivityThread.java
public static void main(String[] args){
....
Looper.prepareMainLooper();
....
}
// code Looper.java
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
Looper類中,prepareMainLooper方法和prepare方法的差別就是是否需要對sMainLooper這個靜態(tài)量置位吆玖。各位看官是否被繞懵了淤袜?不要緊,非墨帶大家在屢一下思路。
- 首先,Android進程啟動的時候,會調(diào)用Looper.prepareMainLooper()方法來給UI線程準(zhǔn)備一個默認(rèn)Looper
- 當(dāng)我們直接生成一個Handler對象的時候(UI線程下),Handler會通過調(diào)用Looper.myLooper();獲取當(dāng)前線程中的Looper對象,并存在自己的成員變量Looper中
- Handler對象獲取Looper對象中的MessageQueue對象,并存在自己的mQueue變量
- 當(dāng)外界通過Handler發(fā)送消息的時候,實際上就是往這個mQueue中入隊消息
//code Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Handler通過enqueueMessage方法將消息加入消息隊列,而MessageQueue通過enqueueMessage來接受消息衰伯。上面我們提到了,Handler系統(tǒng)的總體結(jié)構(gòu)是要完成一個生產(chǎn)者消費者的消息隊列機制,而Handler在實現(xiàn)這個機制的方案铡羡,非常類似我們的BlockQueue。當(dāng)時Handler和MessageQueue系統(tǒng)很明顯沒有直接采用BlockQueue的API意鲸。那么MessageQueue是怎么做的呢烦周?
boolean enqueueMessage(Message msg, long when) {
....
synchronized (this) {
....
Message p = mMessages;//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 {
....
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;//構(gòu)建單向隊列
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
上面代碼很好理解,大致就是將你的消息放入一個單向鏈表隊列中去。最后通過nativeWake(mPtr)喚醒一個東西怎顾。那么mPtr是什么呢读慎?為何要喚醒它?
實際上mPtr是一個native層的一個指針變量,為了說明MessageQueue的消息隊列機制,我們只讀Java層的代碼是不夠的,我們先來看下MessageQueue的java層代碼:
//code MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
我們看到,實際上,mPtr是通過調(diào)用nativeInit方法返回的一個native指針,我們來看下native層MessageQueue的代碼:
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();//生成一個NativeMessageQueue對象
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
//返回NativeMessageQueue對象指針
}
可以看出,在我們的native層,實際上生成了一個NativeMessageQueue對象,而這個對象的指針,由native方法返回給上層槐雾。那么NativeMessageQueue對象又做了什么呢夭委?
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
....
mLooper = Looper::getForThread();//線程內(nèi)唯一的Looper對象
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
各位看官是否似曾相識,NativeMessageQueue又寫了一個Looper對象,而且也是線程內(nèi)唯一的。希望大家一定要濾清這兩個Looper對象,他們不在同一個層次,但是在同一個進程中,且都線程唯一募强。當(dāng)上面我們只是說了生產(chǎn)者這邊的情況,如果我們脫離了消費者,其實我們并不能說明MessageQueue的消息隊列是如何完成消息傳遞通訊的株灸。那么MessageQueue的消費者是誰呢崇摄?
實際上,我們一直在說它,就是Java層的Looper對象,它就像它的名字一樣,就是一個始終循環(huán)的對象,而這種循環(huán)是通過loop方法實現(xiàn):
//code Looper.java
public static void loop() {
...
for (;;) {
Message msg = queue.next(); // 消費消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
....
msg.target.dispatchMessage(msg);
...
}
....
msg.recycle();
}
...
}
我們看到,Looper對象通過MessageQueue的next方法來獲取并消費消息:
//code MessageQueue.java
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
for (;;) {
....
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;
}
....
}
....
}
}
Java層的MessageQueue通過調(diào)用native層的nativePollOnce方法來獲取消息,而在native層的代碼又調(diào)用了我們上面所說的NativeMessageQueue對象的pollOnce方法,而NativeMessageQueue又會調(diào)用native層的Looper對象的pollOnce方法:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
....
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
...
mLooper->pollOnce(timeoutMillis);
....
}
//code Looper.h
int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
inline int pollOnce(int timeoutMillis) {
return pollOnce(timeoutMillis, NULL, NULL, NULL);
}
pollOnce(int)方法最終會調(diào)用到 pollOnce(int, int, int, void**),在pollOnce(int)方法中會將后面的參數(shù)都給默認(rèn)NULL,pollOnce中又會調(diào)用pollInner方法
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
....
result = pollInner(timeoutMillis);
}
int Looper::pollInner(int timeoutMillis) {
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
mPolling = false;
// Acquire lock.
mLock.lock();
....
}
不知道看官們累了沒有,我們似乎終于找到了一點門路:epoll_wait函數(shù)。這是屬于linux中epoll族中的函數(shù)慌烧。該函數(shù)類似Java中的nio機制,mEpollFd是epoll文件描述符逐抑,而epoll_wait用于監(jiān)聽發(fā)生在描述符mEpollFd上的事件。返回值代表所監(jiān)聽到的事件數(shù)目屹蚊。
//code pollInner();
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken();//等待ing
} else {
....
}
} else {
....
}
}
void Looper::awoken() {
uint64_t counter;
TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));//通過read mWakeEventFd中的數(shù)據(jù)陷入等待
}
在Epoll收到EPOLLIN陷入awoken等待中,等待的方式,是通過讀取mWakeEventFd文件中的64位數(shù)據(jù)厕氨。筆者再給大家回顧一下這個過程:
- Looper對象通過loop方法消費MesssageQueue中的消息,而獲取消息的方法是通過MesssageQueue.next()方法
- MesssageQueue.next()方法中會調(diào)用native層NativeMessageQueue對象的pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) 方法
- NativeMessageQueue調(diào)用Native層Looper對象的pollInner()方法
- Looper通過監(jiān)聽epoll文件描述符來監(jiān)聽事件
- Looper中的Epoll得到EPOLLIN信號,將調(diào)用awoken()等待
那么消費者陷入等待,生產(chǎn)者是如何通知消費者的呢?上面我們提到了生產(chǎn)者是Handler對象,因為是它往MessageQueue中增加消息的,而增加消息的方法是通過調(diào)用MessageQueue的boolean enqueueMessage(Message msg, long when)方法汹粤。
boolean enqueueMessage(Message msg, long when) {
...
if (needWake) {
nativeWake(mPtr);
}
...
}
在MessageQueue的enqueueMessage方法中會調(diào)用nativeWake方法,而native層的nativeWake會調(diào)用native層Looper的wake方法命斧。
void NativeMessageQueue::wake() {
mLooper->wake();
}
而通過往mWakeEventFd文件中寫入數(shù)據(jù),實現(xiàn)了上面Looper對象的喚醒
void Looper::wake() {
....
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
....
}
好了,我們費了那么大的周折講了在Android中消息隊列如何實現(xiàn)阻塞和釋放效果。我們再回頭看看Java層的Looper對象,獲取了一個Message對象以后,又是如何處理的:
public static void loop() {
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
....
try {
msg.target.dispatchMessage(msg);//target is Handler
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
....
}
當(dāng)Looper(Java)從消息隊列MessageQueue中通過next方法獲取一個消息Message后,將調(diào)用Message.target的dispatchMessage方法嘱兼。那么這個target變量又是什么呢冯丙?其實,這個target變量就是我們最早登場的主角Handler。我們看下這個Handler是如何被記錄在Message變量中的:
//code Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//記錄Handler到Message對象的target屬性中去
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
如上面代碼所示,Handler在調(diào)用自身的enqueueMessage方法的時候,會將自己作為target記錄到需要放入MessageQueue的Message對象中去遭京。我們再回到Looper的代碼中去,Looper調(diào)用了Handler的dispatchMessage(Message)方法來處理消息:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//通過Handler.post(Runnable方式執(zhí)行)
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
當(dāng)消息通過Handler.post(Runnable)方式發(fā)出的時候,Runnable對象會被記錄成Message的callback成員。如果以其他的方式進行,那么將被Handler自己的mCallback對象攔截泞莉。如果沒有這個對象,或者mCallback不處理這個消息,將調(diào)用handleMessage(Message)處理消息哪雕。而這個方法是一個空方法,將由子類完成消息處理。實際上,文章到這里,Handler,MessageQueue,Message,Looper幾個對象間的關(guān)系已經(jīng)很清楚了,基本就是Handler為生產(chǎn)者,MessageQueue為生產(chǎn)隊列,Message為產(chǎn)品,Looper為消費鲫趁。而阻塞和喚醒操作由native的Looper對象通過epoll方式實現(xiàn)斯嚎。那么還有一個問題:
我們的Handler作為target變量被存放在Message對象中,Message對象如何保證我們的Handler對象的正常釋放的呢?
- 回答這個問題我們需要回到Looper(java)的loop代碼中去尋找:
//code Looper.loop
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();//執(zhí)行回收操作
當(dāng)msg被處理完成之后,msg將調(diào)用recycleUnchecked方法進行回收,就是在這個函數(shù)中,Message對象被清空,并且放入緩沖池中:
void recycleUnchecked() {
....
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {//放入緩沖池,減少碎片
next = sPool;
sPool = this;
sPoolSize++;
}
}
....
}