該篇文章主要著重講解Message的發(fā)送與取出的具體邏輯細(xì)節(jié)涤垫。在此之前,我們先回顧一下Handler發(fā)送消息的具體流程扰法。
消息的發(fā)送
我們都知道當(dāng)調(diào)用Handler發(fā)送消息的時(shí)候枝冀,不管是調(diào)用sendMessage,sendEmptyMessage,sendMessageDelayed還是其他發(fā)送一系列方法。最終都會(huì)調(diào)用sendMessageDelayed(Message msg, long delayMillis)方法构捡。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
該方法會(huì)調(diào)用sendMessageAtTime()方法。其中第二個(gè)參數(shù)是執(zhí)行消息的時(shí)間豁遭,是通過從開機(jī)到現(xiàn)在的毫秒數(shù)(手機(jī)睡眠的時(shí)間不包括在內(nèi))+ 延遲執(zhí)行的時(shí)間叭喜。這里不使用System.currentTimeMillis() 贺拣,是因?yàn)樵摃r(shí)間是可以修改的蓖谢。會(huì)導(dǎo)致延遲消息等待時(shí)間不準(zhǔn)確。該方法內(nèi)部會(huì)調(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);
}
這里獲取到線程中的MessageQueue對(duì)象mQueue(在構(gòu)造函數(shù)通過Looper獲得的),并調(diào)用enqueueMessage()方法涡匀,enqueueMessage()方法最終內(nèi)部會(huì)調(diào)用MessageQueue的enqueueMessage()方法,那現(xiàn)在我們就直接看MessageQueue中把消息加入消息隊(duì)列中的方法盯腌。
消息的加入
當(dāng)通過handler調(diào)用一系列方法如sendMessage()、sendMessageDelayed()方法時(shí)陨瘩,最終會(huì)調(diào)用MessageQueue的enqueueMessage()方法⊥蠊唬現(xiàn)在我們就來看看,消息是怎么加入MessageQueue(消息隊(duì)列)中去的舌劳。
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) {
//如果當(dāng)前消息循環(huán)已經(jīng)結(jié)束帚湘,直接退出
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;
//如果隊(duì)列中沒有消息,或者當(dāng)前進(jìn)入的消息比消息隊(duì)列中的消息等待時(shí)間短甚淡,那么就放在消息隊(duì)列的頭部
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 {
//判斷喚醒條件大诸,當(dāng)前當(dāng)前消息隊(duì)列頭部消息是屏障消息,且當(dāng)前插入的消息為異步消息
//且當(dāng)前消息隊(duì)列處于無消息可處理的狀態(tài)
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//循環(huán)遍歷消息隊(duì)列贯卦,把當(dāng)前進(jìn)入的消息放入合適的位置(比較等待時(shí)間)
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;
}
//調(diào)用nativeWake资柔,以觸發(fā)nativePollOnce函數(shù)結(jié)束等待
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
這里大家肯定注意到了nativeWake()方法,這里先不對(duì)該方法進(jìn)行詳細(xì)的描述撵割,下文會(huì)對(duì)其解釋及介紹贿堰。
其實(shí)通過代碼大家就應(yīng)該發(fā)現(xiàn)了,在將消息加入到消息隊(duì)列中時(shí)啡彬,已經(jīng)將消息按照等待時(shí)間進(jìn)行了排序官边。排序分為兩種情況(這里圖中的message.when是與當(dāng)前的系統(tǒng)的時(shí)間差):
-
第一種:如果隊(duì)列中沒有消息沸手,或者當(dāng)前進(jìn)入的消息比消息隊(duì)列中頭部的消息等待時(shí)間短,那么就放在消息隊(duì)列的頭部
-
第二種:反之注簿,循環(huán)遍歷消息隊(duì)列契吉,把當(dāng)前進(jìn)入的消息放入合適的位置(比較等待時(shí)間)
綜上,我們了解了在我們使用Handler發(fā)送消息時(shí)诡渴,當(dāng)消息進(jìn)入到MessageQueue(消息隊(duì)列)中時(shí)捐晶,已經(jīng)按照等待時(shí)間進(jìn)行了排序,且其頭部對(duì)應(yīng)的消息是Loop即將取出的消息妄辩。
獲取消息
我們都知道消息的取出惑灵,是通過Loooper.loop()方法,其中l(wèi)oop()方法內(nèi)部會(huì)調(diào)用MessageQueue中的next()方法眼耀。那下面我們就直接來看next()方法英支。
Message next() {
//如果退出消息消息循環(huán),那么就直接退出
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();
}
//執(zhí)行native層消息機(jī)制層,
//timeOutMillis參數(shù)為超時(shí)等待時(shí)間哮伟。如果為-1干花,則表示無限等待,直到有事件發(fā)生為止楞黄。
//如果值為0池凄,則無需等待立即返回。該方法可能會(huì)阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//獲取系統(tǒng)開機(jī)到現(xiàn)在的時(shí)間鬼廓,如果使用System.currentMillis()會(huì)有誤差肿仑,
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;//頭部消息
//判斷是否是柵欄,同時(shí)獲取消息隊(duì)列最近的異步消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//獲取消息碎税,判斷等待時(shí)間尤慰,如果還需要等待則等待相應(yīng)時(shí)間后喚醒
if (msg != null) {
if (now < msg.when) {//判斷當(dāng)前消息時(shí)間,是不是比當(dāng)前時(shí)間大雷蹂,計(jì)算時(shí)間差
// 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 {
// 不需要等待時(shí)間或者等待時(shí)間已經(jīng)到了伟端,那么直接返回該消息
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 {
//沒有更多的消息了
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//判斷是否已經(jīng)退出了
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//獲取空閑時(shí)處理任務(wù)的handler 用于發(fā)現(xiàn)線程何時(shí)阻塞等待更多消息的回調(diào)接口。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//如果空閑時(shí)處理任務(wù)的handler個(gè)數(shù)為0萎河,繼續(xù)讓線程阻塞
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
//判斷當(dāng)前空閑時(shí)處理任務(wù)的handler是否是為空
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//只有第一次迭代的時(shí)候荔泳,才會(huì)執(zhí)行下面代碼
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final 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);
}
//如果不保存空閑任務(wù),執(zhí)行完成后直接刪除
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 重置空閑的handler個(gè)數(shù)虐杯,因?yàn)椴恍枰貜?fù)執(zhí)行
pendingIdleHandlerCount = 0;
//當(dāng)執(zhí)行完空閑的handler的時(shí)候玛歌,新的native消息可能會(huì)進(jìn)入,所以喚醒Native消息機(jī)制層
nextPollTimeoutMillis = 0;
}
}
這里大家直接看MessageQueue的next()法肯定會(huì)很懵逼擎椰。媽的支子,這個(gè)nativePollOnce()方法是什么鬼,為毛它會(huì)阻塞呢达舒?這個(gè)msg.isAsynchronous()判斷又是怎么回事值朋?媽的這個(gè)邏輯有點(diǎn)亂理解不了啊叹侄。大家不要慌,讓我們帶著這幾個(gè)問題來慢慢分析昨登。
Native消息機(jī)制
其實(shí)在Android 消息處理機(jī)制中趾代,不僅包括了Java層的消息機(jī)制處理,還包括了Native消息處理機(jī)制(與我們知道的Handler機(jī)制一樣丰辣,也擁有Handler撒强、Looper、MessageQueue)笙什。這里我們不講Native消息機(jī)制的具體代碼細(xì)節(jié)飘哨,如果有興趣的小伙伴,請(qǐng)查看—–>深入理解Java Binder和MessageQueue
這里我們用一張圖來表示Native消息與Jave層消息的關(guān)系(這里為大家提供了Android源碼琐凭,大家可以按需下載)芽隆,具體細(xì)節(jié)如下圖所示:
(這里我用的別人的圖,如有侵權(quán)统屈,請(qǐng)聯(lián)系我胚吁,馬上刪除)。
其實(shí)我們也可以從Java層中的MessageQueue中幾個(gè)方法就可以看出來鸿吆。其中聲明了幾個(gè)本地的方法囤采。
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
特別是在MessageQueue構(gòu)造方法中述呐。
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();//mPtr 其實(shí)是Native消息機(jī)制中MessageQueue的地址惩淳。
}
在Java層中MessageQueue在初始化的時(shí)候,會(huì)調(diào)用本地方法去創(chuàng)建Native MessageQueue乓搬。并通過mPrt保存了Native中的MessageQueue的地址思犁。
Native消息機(jī)制與Java層的消息機(jī)制有什么關(guān)系
想知道有什么關(guān)系,我們需要查看frameworks\base\core\jni\android_os_MessageQueue.cpp文件进肯,
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
其實(shí)nativeInit()方法很簡(jiǎn)單激蹲,初始化NativeMessageQueue對(duì)象然后返回其地址。現(xiàn)在我們繼續(xù)查看NativeMessageQueue的構(gòu)造函數(shù)江掩。
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
哇学辱,我們看見了我們熟悉的”Looper“,這段代碼其實(shí)很好理解。Native Looper調(diào)用靜態(tài)方法getForThread()环形,獲取當(dāng)前線程中的Looper對(duì)象策泣。如果為空,則創(chuàng)建Native Looper對(duì)象抬吟。這里大家肯定會(huì)有個(gè)疑問萨咕。當(dāng)前線程是指什么線程呢?想知道到底綁定是什么線程火本,我們需要進(jìn)入Native Looper中查看setForThread()與getForThread()兩個(gè)方法危队。
getForThread()從線程中獲取設(shè)置的變量
/**
* Returns the looper associated with the calling thread, or NULL if
* there is not one.
*/
sp<Looper> Looper::getForThread() {
int result = pthread_once(& gTLSOnce, initTLSKey);
LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
return (Looper*)pthread_getspecific(gTLSKey);
}
這里pthread_getspecific()機(jī)制類似于Java層的ThreadLocal中的get()方法,是從線程中獲取key值對(duì)應(yīng)的數(shù)據(jù)聪建。其中通過我們可以通過注釋就能明白,Native Looper是存儲(chǔ)在本地線程中的,而對(duì)應(yīng)的線程茫陆,就是調(diào)用它的線程金麸,而我們是在主線程中調(diào)用的。故Native Looper與主線程產(chǎn)生了關(guān)聯(lián)簿盅。那么相應(yīng)的setForThread()也是對(duì)主線程進(jìn)行操作的了钱骂。接著看setForThread()方法。
setForThread()從線程中設(shè)置變量
/**
* Sets the given looper to be associated with the calling thread.
* If another looper is already associated with the thread, it is replaced. *
* If "looper" is NULL, removes the currently associated looper.
*/
void Looper::setForThread(const sp<Looper>& looper) {
sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
if (looper != NULL) {
looper->incStrong((void*)threadDestructor);
}
pthread_setspecific(gTLSKey, looper.get());
if (old != NULL) {
old->decStrong((void*)threadDestructor);
}
}
這里pthread_setspecific()機(jī)制類似于Java層的ThreadLocal中的set()方法挪鹏。通過注釋我們明白將Native looper放入調(diào)用線程见秽,如果已經(jīng)存在,就替換讨盒。如果為空就刪除解取。
nativePollOnce()方法為什么會(huì)導(dǎo)致主線程阻塞?
經(jīng)過上文的討論與分析返顺,大家現(xiàn)在已經(jīng)知道了禀苦,在Android消息機(jī)制中不僅有 Java層的消息機(jī)制,還有Native的消息機(jī)制遂鹊。既然要出里Native的消息機(jī)制振乏。那么肯定有一個(gè)處理消息的方法。那么調(diào)用本地消息機(jī)制消息的方法必然就是nativePollOnce()方法秉扑。
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
在nativePollOnce()方法中調(diào)用nativeMessageQueue的pollOnce()方法慧邮,我們接著走。
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
這里我們發(fā)現(xiàn)pollOnce(timeoutMillis)內(nèi)部調(diào)用的是Natave looper中的 pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)方法舟陆。繼續(xù)看误澳。
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis);
}
}
由于篇幅的限制,這里就簡(jiǎn)單介紹一哈pollOnce()方法秦躯。該方法會(huì)一直等待Native消息忆谓,其中 timeOutMillis參數(shù)為超時(shí)等待時(shí)間。如果為-1踱承,則表示無限等待倡缠,直到有事件發(fā)生為止。如果值為0茎活,則無需等待立即返回昙沦。 那么既然nativePollOnce()方法有可能阻塞,那么根據(jù)上文我們討論的MessageQueue中的enqueueMessage中的nativeWake()方法妙色。大家就應(yīng)該了然了桅滋。nativeWake()方法就是喚醒Native消息機(jī)制不再等待消息而直接返回。
nativePollOnce()一直循環(huán)為毛不造成主線程的卡死?
到了這里丐谋,其實(shí)大家都會(huì)有個(gè)疑問芍碧,如果當(dāng)前主線程的MessageQueue沒有消息時(shí),程序就會(huì)便阻塞在loop的queue.next()中的nativePollOnce()方法里号俐,一直循環(huán)那么主線程為什么不卡死呢泌豆?這里就涉及到Linux pipe/epoll機(jī)制,此時(shí)主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài)吏饿,直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生踪危,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作。這里采用的epoll機(jī)制猪落,是一種IO多路復(fù)用機(jī)制贞远,可以同時(shí)監(jiān)控多個(gè)描述符,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w)笨忌,則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮骼吨伲举|(zhì)同步I/O,即讀寫是阻塞的官疲。 所以說袱结,主線程大多數(shù)時(shí)候都是處于休眠狀態(tài),并不會(huì)消耗大量CPU資源途凫。–摘自Gityuan知乎回答
如果大家想消息了解Native 消息機(jī)制的處理機(jī)制垢夹,請(qǐng)查看—–>深入理解Java Binder和MessageQueue
屏障消息與異步消息
屏障消息
在next()方法中,有一個(gè)屏障的概念(message.target ==null為屏障消息)维费,如下代碼:
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());
}
其實(shí)通過代碼果元,當(dāng)出現(xiàn)屏障的時(shí)候,會(huì)濾過同步消息掩完,而是直接獲取其中的異步消息并返回噪漾。如下圖所示:
在Hadnler無參的構(gòu)造函數(shù)中硼砰,默認(rèn)設(shè)置的消息都是同步的且蓬。那我們就可以知道在Android中消息分為了兩種,一種是同步消息题翰,另一種是異步消息恶阴。在官方的解釋中,異步消息通常代表著中斷豹障,輸入事件和其他信號(hào)冯事,這些信號(hào)必須獨(dú)立處理,即使其他工作已經(jīng)暫停血公。
異步消息
要設(shè)置異步消息昵仅,直接調(diào)用Message的setAsynchronous()方法,方法如下:
/**
* Sets whether the message is asynchronous, meaning that it is not
* subject to {@link Looper} synchronization barriers.
* <p>
* Certain operations, such as view invalidation, may introduce synchronization
* barriers into the {@link Looper}'s message queue to prevent subsequent messages
* from being delivered until some condition is met. In the case of view invalidation,
* messages which are posted after a call to {@link android.view.View#invalidate}
* are suspended by means of a synchronization barrier until the next frame is
* ready to be drawn. The synchronization barrier ensures that the invalidation
* request is completely handled before resuming.
* </p><p>
* Asynchronous messages are exempt from synchronization barriers. They typically
* represent interrupts, input events, and other signals that must be handled independently
* even while other work has been suspended.
* </p><p>
* Note that asynchronous messages may be delivered out of order with respect to
* synchronous messages although they are always delivered in order among themselves.
* If the relative order of these messages matters then they probably should not be
* asynchronous in the first place. Use with caution.
* </p>
*
* @param async True if the message is asynchronous.
*
* @see #isAsynchronous()
*/
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
大家可以看到,設(shè)置異步消息摔笤,官方文檔對(duì)其有詳細(xì)的說明够滑,側(cè)面體現(xiàn)出了異步消息的重要性,那下面我就帶著大家一起來理一理官方的注釋說明吕世。
- 如果設(shè)置了異步消息彰触,異步消息將不會(huì)受到屏障的影響(從next()方法中,我們已經(jīng)了解了命辖,當(dāng)出現(xiàn)屏障的時(shí)候况毅,同步消息會(huì)直接被過濾。直接返回最近的異步消息)
- 在某些操作中尔艇,例如視圖進(jìn)行invalidation(視圖失效尔许,進(jìn)行重繪),會(huì)引入屏障消息(也就是將message.target ==null的消息放入消息隊(duì)列中)终娃,已防止后續(xù)的同步消息被執(zhí)行母债。同時(shí)同步消息的執(zhí)行會(huì)等到視圖重繪完成后才會(huì)執(zhí)行。
有哪些操作是異步消息呢尝抖?
這里我就直接通過ActivityThread中的幾個(gè)異步消息給大家做一些簡(jiǎn)單介紹毡们。這里我就不用代碼展示了,用圖片來表示更清晰明了昧辽。
在ActivityThread中衙熔,有一個(gè)sendMessage()多個(gè)參數(shù)方法。我們明顯的看出搅荞,有四個(gè)消息是設(shè)置為異步消息的红氯。DUMP_SERVICE、DUMP_HEAP咕痛、DUMP_ACTIVITY痢甘、DUMP_PROVIDER。從字面意思就可以看出來茉贡∪ぃ回收service、回收堆內(nèi)存腔丧、回收Activity放椰、回收Provider都屬于異步消息。
屏障消息發(fā)送的時(shí)機(jī)
那么Android中在哪些情況下會(huì)發(fā)生屏障消息呢愉粤?其實(shí)最為常見的就是在我們界面進(jìn)行繪制的時(shí)候砾医,如在ViewRootImpl.scheduleTraversals()中。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//發(fā)送屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
在調(diào)用scheduleTraversals()方法時(shí)衣厘,我們發(fā)現(xiàn)會(huì)發(fā)生一個(gè)屏障過去如蚜。具體代碼如下:
private int postSyncBarrier(long when) {
synchronized (this) {
//記錄屏障消息的個(gè)數(shù)
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
//按照消息隊(duì)列的等待時(shí)間,將屏障按照順序插入
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
這里我們直接將圍欄放在了消息隊(duì)列中,同時(shí)重要的是我們并沒有直接設(shè)置target,也就是tartget =null错邦。其實(shí)現(xiàn)在我們可以想象涎显,我們當(dāng)我們正在進(jìn)行界面的繪制的時(shí)候,我們是不希望有其他操作的兴猩,這個(gè)時(shí)候期吓,要排除同步消息操作,也是可能理解的倾芝。
IdleHandler(MessageQueuqe空閑時(shí)執(zhí)行的任務(wù))
在MessageQueue中的next()方法讨勤,出現(xiàn)了IdleHandler(MessageQueuqe空閑時(shí)執(zhí)行的任務(wù)),查看MessageQueue中IdleHander接口的說明:
/**
* Callback interface for discovering when a thread is going to block
* waiting for more messages.
*/
public static interface IdleHandler {
/**
* Called when the message queue has run out of messages and will now
* wait for more. Return true to keep your idle handler active, false
* to have it removed. This may be called if there are still messages
* pending in the queue, but they are all scheduled to be dispatched
* after the current time.
*/
boolean queueIdle();
}
當(dāng)線程正在等待更多消息時(shí)晨另,會(huì)回調(diào)該接口潭千,同時(shí)queueIdl()方法,會(huì)在消息隊(duì)列執(zhí)行完所有的消息等待且在等待更多消息時(shí)會(huì)被調(diào)用借尿,如果返回true,表示該任務(wù)會(huì)在消息隊(duì)列等待更多消息的時(shí)候繼續(xù)執(zhí)行刨晴,如果為false,表示該任務(wù)執(zhí)行完成后就會(huì)被刪除路翻,不再執(zhí)行狈癞。
其中MessageQueue通過使用addIdleHandler(@NonNull IdleHandler handler) 方法添加空閑時(shí)任務(wù)。具體代碼如下:
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
既然MessageQueue可以添加空閑時(shí)任務(wù)茂契,那么我們就看看最為明顯的ActivityThread中聲明的GcIdler蝶桶。在ActivityThread中的H收到GC_WHEN_IDLE消息后,會(huì)執(zhí)行scheduleGcIdler掉冶,將GcIdler添加到MessageQueue中的空閑任務(wù)集合中真竖。具體如下:
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
//添加GC任務(wù)
Looper.myQueue().addIdleHandler(mGcIdler);
}
mH.removeMessages(H.GC_WHEN_IDLE);
}
ActivityThread中GcIdler的詳細(xì)聲明:
//GC任務(wù)
final class GcIdler implements MessageQueue.IdleHandler {
@Override
public final boolean queueIdle() {
doGcIfNeeded();
//執(zhí)行后,就直接刪除
return false;
}
}
// 判斷是否需要執(zhí)行垃圾回收厌小。
void doGcIfNeeded() {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
//獲取上次GC的時(shí)間
if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
//Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!");
BinderInternal.forceGc("bg");
}
}
GcIdler方法理解起來很簡(jiǎn)單恢共、就是獲取上次GC的時(shí)間,判斷是否需要GC操作璧亚。如果需要?jiǎng)t進(jìn)行GC操作讨韭。這里ActivityThread中還聲明了其他空閑時(shí)的任務(wù)。如果大家對(duì)其他空閑任務(wù)感興趣涨岁,可以自行研究拐袜。
什么時(shí)候喚醒主線程呢?
通過上文的了解梢薪,大家已經(jīng)知道了Native的消息機(jī)制可能會(huì)導(dǎo)致主線程阻塞,那么喚醒Native消息機(jī)制(讓Native消息機(jī)制不在等待Native消息尝哆,也就是nativePollOnce()方法返回)在整個(gè)Android的消息機(jī)制中尤為重要秉撇,這里放在這里給大家講是因?yàn)閱拘训臈l件涉及到屏障消息與空閑任務(wù)。大家理解了這兩個(gè)內(nèi)容后再來理解喚醒的時(shí)機(jī)就相對(duì)容易一點(diǎn)了,這里我們分別對(duì)喚醒的兩個(gè)時(shí)機(jī)進(jìn)行講解琐馆。
在添加消息到消息隊(duì)列中
boolean enqueueMessage(Message msg, long when) {
//...省略部分代碼
synchronized (this) {
//...省略部分代碼
msg.markInUse();
msg.when = when;
Message p = mMessages;//頭部消息
boolean needWake;
//如果隊(duì)列中沒有消息规阀,或者當(dāng)前進(jìn)入的消息比消息隊(duì)列中的消息等待時(shí)間短,那么就放在消息隊(duì)列的頭部
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 {
//判斷喚醒條件瘦麸,當(dāng)前消息隊(duì)列頭部消息是屏障消息谁撼,且當(dāng)前插入的消息為異步消息
//且當(dāng)前消息隊(duì)列處于無消息可處理的狀態(tài)
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//循環(huán)遍歷消息隊(duì)列,把當(dāng)前進(jìn)入的消息放入合適的位置(比較等待時(shí)間)
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;
}
//調(diào)用nativeWake滋饲,以觸發(fā)nativePollOnce函數(shù)結(jié)束等待
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
上述代碼厉碟,我們很明顯的看見Native消息機(jī)制的喚醒,受到needWake這個(gè)變量影響屠缭,needWake ==true是在兩個(gè)條件下箍鼓。
- 第一個(gè):如果當(dāng)前消息按照等待時(shí)間排序是在消息隊(duì)列的頭部, needWake = mBlocked,且mBlocked會(huì)在當(dāng)前消息隊(duì)列中沒有消息可以處理呵曹,且沒有空閑任務(wù)的條件下為true(mBlocked變量的賦值會(huì)在下文講解)款咖。
- 第二個(gè):如果當(dāng)前mBlocked=true(第一個(gè)條件判斷),且消息隊(duì)列頭部消息是屏障消息奄喂,同時(shí)當(dāng)前插入的消息為異步消息的條件铐殃。needWake = true。
在空閑任務(wù)完成的時(shí)候喚醒
Message next() {
//...省略部分代碼
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
//...省略部分代碼
//執(zhí)行native層消息機(jī)制層,
//timeOutMillis參數(shù)為超時(shí)等待時(shí)間跨新。如果為-1背稼,則表示無限等待,直到有事件發(fā)生為止玻蝌。
//如果值為0蟹肘,則無需等待立即返回。該方法可能會(huì)阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//...省略部分代碼
//獲取空閑時(shí)處理任務(wù)的handler 用于發(fā)現(xiàn)線程何時(shí)阻塞等待更多消息的回調(diào)接口俯树。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//如果消息隊(duì)列中沒有消息可以處理帘腹,且沒有空閑任務(wù),那么就繼續(xù)等待消息
mBlocked = true;
continue;
}
//...省略部分代碼
}
//...省略執(zhí)行空閑任務(wù)代碼
// 重置空閑的handler個(gè)數(shù)许饿,因?yàn)椴恍枰貜?fù)執(zhí)行
pendingIdleHandlerCount = 0;
//當(dāng)執(zhí)行完空閑的handler的時(shí)候阳欲,新的native消息可能會(huì)進(jìn)入,所以喚醒Native消息機(jī)制層
nextPollTimeoutMillis = 0;
}
}
這里我們可以看到 mBlocked = true的條件是在消息隊(duì)列中沒有消息可以處理陋率,且也沒有空閑任務(wù)的情況下球化。也就是當(dāng)前mBlocked = true會(huì)影響到MessageQueue中enqueueMessage()方法是否喚醒主線程。
如果當(dāng)前空閑任務(wù)完成后瓦糟,會(huì)將nextPollTimeoutMillis 置為0筒愚,如果nextPollTimeoutMillis =0,會(huì)導(dǎo)致nativePollOnce直接返回菩浙,也就是會(huì)直接喚醒主線程(喚醒Native消息機(jī)制層)巢掺。
MessageQueue取出消息整體流程
到目前為止句伶,大家已經(jīng)對(duì)整個(gè)消息的發(fā)送與取出有一個(gè)大概的了解了。這里我著重對(duì)MessageQueue取消息的流程畫了一個(gè)簡(jiǎn)單的流程圖陆淀。希望大家根據(jù)對(duì)取消息有個(gè)更好的理解考余。
總結(jié)
- Handler在發(fā)消息時(shí),MessageQueue已經(jīng)對(duì)消息按照了等待時(shí)間進(jìn)行了排序轧苫。
- MessageQueue不僅包含了Java層消息機(jī)制同時(shí)包含Native消息機(jī)制
- Handler消息分為異步消息與同步消息兩種楚堤。
- MessageQueue中存在“屏障消息“的概念,當(dāng)出現(xiàn)屏障消息時(shí)含懊,會(huì)執(zhí)行最近的異步消息身冬,同步消息會(huì)被過濾。
- MessageQueue在執(zhí)行完消息隊(duì)列中的消息等待更多消息時(shí)绢要,會(huì)處理一些空閑任務(wù)吏恭,如GC操作等。
有用的話重罪,點(diǎn)個(gè)贊吧?( ′???` )比心
點(diǎn)關(guān)注樱哼,收獲更多Android開發(fā)干貨~~