上一章我們講解SystemServer時(shí)涉及到了消息機(jī)制,因此這一章我們先介紹一下消息循環(huán)機(jī)制,幫助大家弄清楚消息循環(huán)的原理,有助于代碼的編寫(xiě)和優(yōu)化萤厅。
Looper-Message-MessageQueue-Handler消息處理機(jī)制
在Android系統(tǒng)有兩個(gè)通信機(jī)制橄抹,一個(gè)是Binder,一個(gè)是消息機(jī)制祈坠,前者是跨進(jìn)程通信害碾,后者是進(jìn)程內(nèi)部通信。消息通信主要包括幾個(gè)部分:
- 消息發(fā)送者和處理者:Handler
- 消息循環(huán)器:Looper
- 消息隊(duì)列:MessageQueue
- 消息:Message
我們先看一個(gè)時(shí)序圖:
圖中赦拘,1-11步是Looper的準(zhǔn)備過(guò)程,12-17步是獲取消息芬沉,處理消息躺同,回收消息的循環(huán)過(guò)程。
下面是一張消息循環(huán)過(guò)程圖丸逸,圖片來(lái)自網(wǎng)絡(luò)博客(blog.mindorks.com)蹋艺,Looper會(huì)通過(guò)loop方法不斷從消息隊(duì)列去取消息,然后交給handler處理黄刚,處理完成就回收消息捎谨,要注意的是只有一個(gè)looper,但是可能有多個(gè)handler:
1憔维、Looper
Looper是一個(gè)循環(huán)器涛救,通過(guò)里面的loop方法不斷去取消息,發(fā)送給Handler進(jìn)行處理业扒。根據(jù)上面時(shí)序圖以及SystemServer啟動(dòng)代碼我們開(kāi)始分析Looper的調(diào)用過(guò)程:
private void run() {
try {
...
// 準(zhǔn)備主線程的Looper
Looper.prepareMainLooper();
...
} finally {
...
}
...
// Loop(循環(huán)) forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我們先看Looper.prepareMainLooper方法:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
上面有段注釋检吆,我翻譯一下,就是:初始化當(dāng)前線程作為一個(gè)looper程储,并把它標(biāo)記為應(yīng)用的主looper蹭沛。這個(gè)looper是被Android環(huán)境(系統(tǒng))創(chuàng)建的,因此你不需要自己調(diào)用這個(gè)方法章鲤。也就是系統(tǒng)創(chuàng)建了這個(gè)looper摊灭,你不需要再創(chuàng)建了。我們接著看里面的內(nèi)容败徊,首先調(diào)用了prepare方法帚呼,需要注意的是Looper.prepare()在每個(gè)線程只允許執(zhí)行一次,該方法會(huì)創(chuàng)建Looper對(duì)象:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {// 確保ThreadLocal中只有一個(gè)Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
上面的ThreadLocal是聲明在類里的集嵌,并且是靜態(tài)的萝挤,因此,隨著類創(chuàng)建了該對(duì)象根欧,get方法是獲取Looper的怜珍,如果能獲取到,則拋出異常凤粗,也就是確保當(dāng)前線程只有一個(gè)Looper酥泛。如果是空今豆,那么我們創(chuàng)建一個(gè)Looper放到里面去。
我們先看一下ThreadLocal:線程本地存儲(chǔ)區(qū)(Thread Local Storage柔袁,簡(jiǎn)稱為TLS)呆躲,每個(gè)線程都有自己的私有的本地存儲(chǔ)區(qū)域,不同線程之間彼此不能訪問(wèn)對(duì)方的TLS區(qū)域捶索。(來(lái)自Android消息機(jī)制1-Handler(Java層))我們看一下它的set和get方法:
ThreadLocal的set方法:
public void set(T value) {
//獲取當(dāng)前線程
Thread t = Thread.currentThread();
// 獲取當(dāng)前線程里的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
如果map不為空插掂,則以鍵值對(duì)放入進(jìn)行存儲(chǔ),此處map不是HashMap腥例,而是其他辅甥,這里不詳細(xì)解釋。如果map為空燎竖,則通過(guò)下面代碼創(chuàng)建map:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocal的get方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
我們看到獲取的時(shí)候也是根據(jù)當(dāng)前線程去獲取的璃弄。因此每個(gè)線程會(huì)保存一個(gè)Looper。
我們接著看Looper的構(gòu)造函數(shù)有哪些操作构回,也就是創(chuàng)建Looper做了哪些處理:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
首先是創(chuàng)建了MessageQueue對(duì)象夏块,接著創(chuàng)建一個(gè)線程,也就是當(dāng)前線程纤掸,currentThread是一個(gè)native方法脐供,我們不再分析,我們看一下MessageQueue創(chuàng)建做了哪些事情:
MessageQueue(boolean quitAllowed) {
// 是否可以退出消息隊(duì)列
mQuitAllowed = quitAllowed;
// 返回底層的MessageQueue對(duì)象的內(nèi)存地址茁肠,如果為空返回0
mPtr = nativeInit();
}
上面的nativeInit是調(diào)用的jni患民,我貼一下代碼,不再解釋:
我們回到prepareMainLooper方法接著看垦梆,如果sMainLooper不為null匹颤,則拋出異常,提示sMainLooper已經(jīng)創(chuàng)建了托猩,如果是null印蓖,那么調(diào)用myLooper方法回去sMainLooper:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
其實(shí)這個(gè)get方法就是我們上面new完Looper放進(jìn)去的,到此prepareMainLooper就完成了京腥,相關(guān)信息也準(zhǔn)備好了赦肃。接下來(lái)就是調(diào)用Looper.loop方法,方法下面是一個(gè)異常公浪,怎么樣才能保證異常不會(huì)拋出他宛,就是loop方法永遠(yuǎn)執(zhí)行不完。是不是只有我們接著看代碼:
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;
...
for (;;) {// 無(wú)限循環(huán)
Message msg = queue.next(); // might block
if (msg == null) { // message為空為結(jié)束信號(hào)欠气,退出循環(huán)
// No message indicates that the message queue is quitting.
return;
}
...
try {
// 將真正的處理工作交給message的target厅各,即handler
msg.target.dispatchMessage(msg);
} finally {
...
}
...
// 回收Message
msg.recycleUnchecked();
}
}
首先是通過(guò)myLooper方法獲取Looper,如果為空预柒,則拋出異常队塘,提示還沒(méi)有調(diào)用Looper.prepare方法袁梗,如果不為空,則通過(guò)looper獲取MessageQueue對(duì)象憔古,然后進(jìn)入for循環(huán)遮怜,因?yàn)閒or語(yǔ)句中沒(méi)有條件,因此該for循環(huán)為無(wú)限循環(huán)鸿市,在這個(gè)循環(huán)中有三件事锯梁,一個(gè)是獲取消息隊(duì)列中的下一個(gè)消息,然后處理該消息焰情,最近處理完消息涝桅,回收消息。這三個(gè)過(guò)程就是Looper的主要作用:取消息烙样,處理消息,回收消息蕊肥。
2.Message
Message是整個(gè)循環(huán)中信息的載體谒获,它是一個(gè)鏈表結(jié)構(gòu),關(guān)于鏈表結(jié)構(gòu)可以參考下面文章:
Android自助餐--Handler消息機(jī)制完全解析--系列
鏈表數(shù)據(jù)結(jié)構(gòu)圖解 和 代碼實(shí)現(xiàn)
基本數(shù)據(jù)結(jié)構(gòu):鏈表(list)
鏈表結(jié)構(gòu)之單鏈表
我們看個(gè)圖:
上面就是一個(gè)示例圖壁却,每個(gè)Message中都有一個(gè)后面Message的引用next批狱,鏈表最后一個(gè)next為空,sPool是第一個(gè)Message展东。但是每個(gè)Message的內(nèi)存地址不是挨著的赔硫,這樣可以占用零碎的內(nèi)存。
我們先來(lái)看Message包含的參數(shù):
public int what;
public int arg1;
public int arg2;
public Object obj;
/*package*/ int flags;
/*package*/ long when;
/*package*/ Handler target;
// 消息隊(duì)列中下一個(gè)消息的引用
/*package*/ Message next;
// sPool這個(gè)變量可以理解為消息隊(duì)列的頭部的指針盐肃,也就是當(dāng)前消息對(duì)象
private static Message sPool;
// sPoolSize是當(dāng)前的消息隊(duì)列的長(zhǎng)度
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
前四個(gè)參數(shù)很熟悉爪膊,不再解釋,flags是一個(gè)標(biāo)簽砸王,表示是否正在使用推盛;when是處理消息的時(shí)間;target就是我們上面提到的Handler谦铃;next是下一個(gè)Message的引用耘成;sPool是一個(gè)靜態(tài)變量,說(shuō)明只有一個(gè)院喜,其實(shí)這個(gè)是消息隊(duì)列的頭消息凡人;sPoolSize是消息隊(duì)列中消息個(gè)數(shù)行瑞;MAX_POOL_SIZE是消息隊(duì)列最大消息數(shù)量。
Message中有多個(gè)用來(lái)獲取Message對(duì)象的obtain復(fù)寫(xiě)方法师妙。因?yàn)楹竺娴膐btain方法都是通過(guò)第一個(gè)obtain方法獲取Message對(duì)象的,因此我們只看第一個(gè)參數(shù)為空的方法:
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
// 避免多線程進(jìn)行爭(zhēng)搶資源骡显,給sPoolSync進(jìn)行加鎖
synchronized (sPoolSync) {
// 如果消息隊(duì)列的頭部不為空疆栏,則可以取出頭部重用
if (sPool != null) {
Message m = sPool;
// 頭部消息取出后曾掂,將sPool指向后面的消息對(duì)象
sPool = m.next;
// next(隊(duì)列尾部)設(shè)置為null
m.next = null;
m.flags = 0; // clear in-use flag
// 消息隊(duì)列長(zhǎng)度減一
sPoolSize--;
return m;
}
}
// 如果消息隊(duì)列的頭部為空,則創(chuàng)建新的Message對(duì)象
return new Message();
}
系統(tǒng)提示盡量用這種方法獲取Message對(duì)象壁顶,避免創(chuàng)建大量新的對(duì)象珠洗,其實(shí)也可以通過(guò)Handler來(lái)獲取Message,這個(gè)我們?cè)趯andler時(shí)候再講若专。
在上面Looper中我們講到最后消息處理完后需要回收许蓖,這個(gè)回收方法recycleUnchecked也在Message類中:
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
// 避免多線程進(jìn)行爭(zhēng)搶資源,給sPoolSync進(jìn)行加鎖
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
// 回收當(dāng)前消息后時(shí)调衰,將sPool消息后移
next = sPool;
// 將當(dāng)前消息放到頭部
sPool = this;
// 隊(duì)列長(zhǎng)度加一
sPoolSize++;
}
}
}
消息回收時(shí)膊爪,將對(duì)應(yīng)消息的標(biāo)簽設(shè)置為使用中,其他標(biāo)簽設(shè)置為空或者默認(rèn)值嚎莉,如果消息隊(duì)列沒(méi)有超過(guò)最大值米酬,那么將sPool賦值給next,將這個(gè)Message賦值給sPool趋箩,消息隊(duì)列長(zhǎng)度加一赃额。也就是將處理完的消息清空,重新放回消息隊(duì)列等待使用叫确。
3.Handler
Handler是發(fā)送消息和處理消息的工具跳芳。我們先看構(gòu)造方法:
public Handler(Callback callback, boolean async) {
...
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;
}
Handler中的looper是獲取當(dāng)前線程中的looper,looper不能為空竹勉,MessageQueue也是looper中的飞盆。
首先是發(fā)送消息,發(fā)送消息的方法很多次乓,我們看一張圖:
我們看到Handler中有多個(gè)發(fā)送消息的方法吓歇,但是最終調(diào)用了enqueueMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
從代碼我們可以看到msg.target就是Handler,也就是在這里進(jìn)行賦值的檬输,然后是調(diào)用MQ(MessageQueue)的enqueueMessage方法照瘾,這個(gè)方法是添加消息隊(duì)列的,具體內(nèi)容我們后面再講丧慈。因此析命,發(fā)送消息就是講消息添加到消息隊(duì)列。我們前面還講過(guò)獲取Message對(duì)象可以通過(guò)Message中的obtain方法逃默,也可以通過(guò)Handler中的方法鹃愤,我們先看一張圖:
Handler是通過(guò)多個(gè)復(fù)寫(xiě)方法obtainMessage來(lái)獲取Message的,只是傳入?yún)?shù)不同完域,我們看一個(gè)沒(méi)有參數(shù)的方法代碼:
public final Message obtainMessage(){
return Message.obtain(this);
}
我們看到其實(shí)還是調(diào)用了Message.obtain方法软吐,并且傳入了this,也就是Handler吟税,通過(guò)Message.obtain方法將Handler賦值給Message中的target凹耙。從這姿现,我們基本對(duì)Handler與Message的關(guān)系基本明確了,獲取Message的方法我們也完全知道了肖抱,因此我們?cè)谝院笥玫臅r(shí)候不需要再去new一個(gè)Message對(duì)象备典,而是通過(guò)obtain方法去獲取,如果有就不需要new了意述,如果沒(méi)有系統(tǒng)會(huì)自己去創(chuàng)建提佣。
4.MessageQueue
MessageQueue是消息隊(duì)列,其實(shí)是管理消息鏈表的荤崇。它主要功能是取出消息--next方法拌屏,將消息加入隊(duì)列--enqueueMessage方法。
我們先看加入消息隊(duì)列方法enqueueMessage术荤,也就是Handler中發(fā)送消息后加入隊(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) {
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;
}
插入消息隊(duì)列有兩種情況倚喂,一種是消息隊(duì)列處于空閑狀態(tài),直接將消息放在消息隊(duì)列前面瓣戚,可能需要喚醒主線程务唐,另一種是消息隊(duì)列處于忙碌狀態(tài),就不需要喚醒带兜,而是根據(jù)消息處理時(shí)間將消息插入到消息隊(duì)列的對(duì)應(yīng)位置中。
第一種狀態(tài):插入隊(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;
}
if語(yǔ)句的三個(gè)條件是:一吨灭、隊(duì)列為空刚照,二、插入消息需要立即處理喧兄,三无畔、插入消息處理時(shí)間比消息隊(duì)列頭消息早,這三個(gè)條件說(shuō)明消息隊(duì)列處于閑置狀態(tài)吠冤,此時(shí)要把消息放置到消息隊(duì)列頭部浑彰,即將插入消息的next指向消息隊(duì)列的頭p,然后將消息隊(duì)列要處理的消息指向插入消息對(duì)象拯辙,最后判斷是否需要喚醒郭变,如果隊(duì)列阻塞則需要喚醒,否則不需要涯保。
第二種狀態(tài):插入隊(duì)列中間或者后面诉濒,這種情況比較復(fù)雜
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;
因?yàn)椴皇窃陉?duì)列頭,所以需要for循環(huán)去查找應(yīng)該的位置夕春,首先將第一個(gè)消息用prev進(jìn)行緩存未荒,然后當(dāng)前消息引用指向下一個(gè)消息對(duì)象,依次類推及志,直到p == null(到隊(duì)列最后)片排,或者當(dāng)前消息觸發(fā)時(shí)間小于后面這個(gè)消息的觸發(fā)時(shí)間寨腔,停止循環(huán),說(shuō)明找到了位置率寡,此時(shí)執(zhí)行最后兩行代碼迫卢,也就是將當(dāng)前出入消息的next指向p,也就是勇劣,如果p==null靖避,則說(shuō)明插入到最后一個(gè),如果不為空比默,則插入到p前面幻捏,然后將前一個(gè)prev的next指向插入的消息,此時(shí)插入成功命咐。最后的if語(yǔ)句中如果需要喚醒消息隊(duì)列篡九,則調(diào)用底層方法nativeWake喚醒消息隊(duì)列開(kāi)始循環(huán)。到此醋奠,消息插入就講完了榛臼。我們上面說(shuō)到loop方法是通過(guò)MessageQueue的next方法取出消息,那么下面我們看一下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 (; ; ) {// 死循環(huán)
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) {// 未到執(zhí)行時(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 {
// 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;
}
// 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.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
mPtr是MessageQueue初始化的時(shí)候通過(guò)調(diào)用底層方法獲取的底層NativeMessageQueue的對(duì)象沛善,如果底層不能初始化則返回0,如果可以初始化返回對(duì)象地址塞祈,此處判斷金刁,如果沒(méi)有初始化也就沒(méi)有底層的NativeMessageQueue對(duì)象,因此返回null议薪。緊接著開(kāi)始for循環(huán)尤蛮,開(kāi)始遍歷消息隊(duì)列,查找需要處理的消息斯议,在這里产捞,如果消息隊(duì)列為空,或者沒(méi)有需要立即處理的消息都要使線程開(kāi)始等待哼御。接著調(diào)用nativePollOnce方法來(lái)查看當(dāng)前隊(duì)列中有沒(méi)有消息坯临,傳入?yún)?shù)nextPollTimeoutMillis表示要等待的時(shí)間,如果nextPollTimeoutMillis為0則說(shuō)明不需要等待恋昼。接著獲取當(dāng)前時(shí)間now尿扯,初始化prevMsg來(lái)緩存消息,初始化msg來(lái)緩存當(dāng)前消息mMessages焰雕,下面if語(yǔ)句判斷消息不為空但target為空衷笋,則說(shuō)明該消息為“同步分隔欄”(關(guān)于“同步分隔欄”請(qǐng)參看聊一聊Android的消息機(jī)制一文),如果該消息為同步分隔欄,則后面的同步消息都不會(huì)被查找辟宗,只能查找異步消息來(lái)處理爵赵,也就是do-while語(yǔ)句中的代碼,如果沒(méi)有同步分割欄或者找到了后面的異步消息(可能沒(méi)有)泊脐,則接著判斷空幻。
如果消息不為空,則還有消息容客,開(kāi)始判斷時(shí)間秕铛,如果當(dāng)前時(shí)間小于下一個(gè)消息的執(zhí)行時(shí)間,說(shuō)明還需要等待缩挑,那么計(jì)算需要等待的時(shí)間nextPollTimeoutMillis但两,如果當(dāng)前時(shí)間不小于當(dāng)前消息執(zhí)行時(shí)間時(shí),并且前一個(gè)消息prevMsg不為空供置,說(shuō)明出現(xiàn)了“同步分隔欄”谨湘,也就是執(zhí)行了do-while代碼,do-while執(zhí)行完芥丧,說(shuō)明找到了異步消息或者遍歷完整個(gè)隊(duì)列沒(méi)有異步消息紧阔,如果有異步消息,此時(shí)prevMsg.next = msg.next续担,也就是跳過(guò)同步消息擅耽,將異步消息msg.next賦值給prevMsg.next,然后將取出的msg的next賦值為null物遇,因?yàn)橐幚砹孙ぃ圆辉僦赶蚝竺骊?duì)列的消息對(duì)象,然后將msg設(shè)置為正在使用挎挖,并且返回,如果prevMsg為空航夺,則說(shuō)明沒(méi)有出現(xiàn)“同步分隔欄”蕉朵,此時(shí)將當(dāng)前消息mMessages的下一個(gè)消息賦值給mMessages,然后將msg.next設(shè)置為空阳掐,就是不再引用始衅,然后設(shè)置為正在使用,返回該消息缭保。
如果消息為空汛闸,則nextPollTimeoutMillis = -1,說(shuō)明沒(méi)有消息了艺骂,則接著向下執(zhí)行诸老,如果退出消息隊(duì)列,則說(shuō)明所有消息都執(zhí)行完了钳恕,最終調(diào)用nativeDestroy方法别伏,如果不退出消息隊(duì)列蹄衷,則要進(jìn)入等待狀態(tài)。如果第一次進(jìn)入厘肮,并且當(dāng)前消息為空或者消息不為空愧口,但是處于等待狀態(tài),那么要獲取IdleHandler個(gè)數(shù)类茂,如果小于等于0耍属,則說(shuō)明沒(méi)有IdleHandler運(yùn)行,調(diào)用continue執(zhí)行下一次循環(huán)巩检,如果IdleHandler個(gè)數(shù)大于0厚骗,但是等待的Handler(mPendingIdleHandlers)為空,則要?jiǎng)?chuàng)建IdleHandler數(shù)組碴巾,將mIdleHandlers放入數(shù)據(jù)溯捆,然后for循環(huán)調(diào)用每個(gè)IdleHandler的queueIdle方法,如果這個(gè)方法返回false厦瓢,則從數(shù)組移除這個(gè)對(duì)象提揍,否則保留改對(duì)象,下次空閑繼續(xù)執(zhí)行煮仇,最后將pendingIdleHandlerCount置為0劳跃,nextPollTimeoutMillis置為0,繼續(xù)下一次循環(huán)浙垫。
那么到此刨仑,整個(gè)循環(huán)就講完了,因?yàn)椴欢瓹++代碼夹姥,所以底層沒(méi)法分析杉武,只能分析framework層代碼,說(shuō)了很多還是需要自己對(duì)比代碼多理解辙售。
5.Handler的使用方法
我們?cè)谑褂肏andler的時(shí)候軟件會(huì)提示我們有問(wèn)題轻抱,那么到底該怎么寫(xiě)Handler呢,我從Stack Overflow找到了答案旦部,在這就分享一下:
首先祈搜,定義一個(gè)靜態(tài)MxHandler繼承Handler,里面使用弱引用:
public abstract class MxHandler<T> extends Handler {
private WeakReference<T> weak;
public MxHandler(T t) {
this.weak = new WeakReference<T>(t);
}
@Override
public void handleMessage(Message msg) {
if (null == weak || null == weak.get()) {
return;
}
handleMessage(msg, weak);
super.handleMessage(msg);
}
protected abstract void handleMessage(Message msg, WeakReference<T> weak);
}
然后我們?cè)賹?xiě)具體的MyHandler繼承這個(gè)MxHandler:
private static final class MyHandler extends MxHandler<HandlerDemo> {
public MyHandler(HandlerDemo handlerDemo) {
super(handlerDemo);
}
@Override
protected void handleMessage(Message msg, WeakReference<HandlerDemo> weak) {
switch (msg.what) {
case 0:
HandlerDemo h = weak.get();
h.doSomething();
break;
default:
break;
}
}
}
這樣我們?cè)贏ctivity中使用是不會(huì)出現(xiàn)內(nèi)存泄漏之類的錯(cuò)誤士八。
參考:
android的消息處理機(jī)制(圖+源碼分析)——Looper,Handler,Message
Android中Thread容燕、Handler、Looper婚度、MessageQueue的原理分析
Android 異步消息處理機(jī)制 讓你深入理解 Looper蘸秘、Handler、Message三者關(guān)系
Android應(yīng)用程序消息處理機(jī)制(Looper、Handler)分析
注
原文地址:Android系統(tǒng)源碼分析--消息循環(huán)機(jī)制
Android開(kāi)發(fā)群:192508518
微信公眾賬號(hào):Code-MX
注:本文原創(chuàng)秘血,轉(zhuǎn)載請(qǐng)注明出處味抖,多謝。