前言
上一篇Handler 使用以及源碼全面解析(一)
簡(jiǎn)述了Handler的使用之后,這篇從源碼上來分析Handler的原理。
分析源碼時(shí)我們可以發(fā)現(xiàn)很多我們未曾注意到的小細(xì)節(jié)。
比如我們都知道,每個(gè)使用Handler的線程都要有自己Looper, 而使用Looper.myLooper()就可以得到當(dāng)前線程的Looper. 怎么這么神奇呢? 那么每個(gè)線程各自的Looper是如何管理的呢?
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new 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));
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
從源碼可以看到,用的是ThreadLocal來保存的,通過ThreadLocal.get()就獲得當(dāng)前線程Looper對(duì)象。所以進(jìn)入Handler的正題之前,不妨先來了解下What is ThreadLoacl。
1、ThreadLocal
每個(gè)線程都可能需要有自己的數(shù)據(jù)副本,比如有的線程需要Looper,有的不需要,用腳指頭想一下,Looper是不可能作為成員變量定義在Thread中。而這種一一對(duì)應(yīng)的映射關(guān)系,很自然而然我(們)想到了map的形式----Map<Thread,Looper>,通用點(diǎn)就用上泛型,Map<Thread, T>
是的,ThreadLocal內(nèi)部大致是Map這種形式,但并不是Map<Thread, T>。我的想法并沒有被谷歌采用,好羞愧。(google:?_?該吃藥了),因?yàn)轱@然如果以Thread為key, 存于一個(gè)靜態(tài)通用的Map中,當(dāng)線程掛了之后,管理不當(dāng)很容易出現(xiàn)內(nèi)存泄漏。
Thread雖然沒有Looper變量,但是它持有ThreadLocalMap變量??!Map中的每個(gè)映射關(guān)系是這樣的---Entry<ThredLocal, T>,即Thread擁有y由很多個(gè)Entry<ThredLocal, T>組成的map,也就是有很多個(gè)T的資源(這表述怎么怪怪的)
而通過ThreadLocal所謂能存取不同線程的副本的原因,正是每次進(jìn)行g(shù)et和set方法時(shí),ThreadLocal都會(huì)用Thread.currentThread()來獲取當(dāng)前線程ThreadLocalMap,把該ThreadLocal作為key,從ThreadLocalMap進(jìn)行相應(yīng)的存取Value操作
ThreadLocal.java
public T get() {
Thread t = Thread.currentThread();
//Thread中的threadLocals變量
ThreadLocalMap map = getMap(t); //return t.threadLocals;
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); // 可以實(shí)現(xiàn)initialValue()方法去設(shè)置初始值
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
這個(gè)ThreadLocalMap還有一點(diǎn)不同的是,并不是我們常見的HashMap。HashMap的setValue原理,我們都知道,Key的hash值對(duì)數(shù)組長(zhǎng)度取余就得到Key在數(shù)組的位置,若該位置已經(jīng)有其它Key存在,那就會(huì)生成一個(gè)指針,與相同位置的形成一條單向直鏈,鏈長(zhǎng)度過長(zhǎng)時(shí)還會(huì)轉(zhuǎn)化為紅黑樹的結(jié)構(gòu)(java 1.8)。
而ThreadLocalMap并不不存在指針,也就沒有鏈。在取余得到的位置被占有后,則會(huì)從該位置起對(duì)數(shù)組進(jìn)行遍歷,知道找到空位為止。
當(dāng)然我們一般情況下我們也不會(huì)去使用ThreadLocal,當(dāng)某些數(shù)據(jù)在以線程為作用域且不同線程具有不同的數(shù)據(jù)副本的時(shí)候,就可以考慮采用ThreadLocal??赡茏x者至今都沒有用過(哈哈,我在業(yè)務(wù)需求上也沒有用過~)
再舉一個(gè)例子,AMS中的使用:
//記錄不同線程的pid和uid
private class Identity {
public final IBinder token;
public final int pid;
public final int uid;
Identity(IBinder _token, int _pid, int _uid) {
token = _token;
pid = _pid;
uid = _uid;
}
}
private static final ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
再多說一句,Android 5.0到7.0之間,Android SDK 中ThreadLocal的代碼跟JDK是不一致的,是所謂優(yōu)化過的,同樣用數(shù)組,偶數(shù)下標(biāo)存Key,奇數(shù)下標(biāo)存Value
values[index] = key //(ThreadLocal<?>.refrence,弱引用)
values[index+1] = value//()
7.0及之后的就保持一致了,真香。
接下里進(jìn)入正題了,那接下來我可要放大招了 要貼源碼了。
Looper是某個(gè)餐廳(Thread)的專職外賣小哥,Handler是某個(gè)點(diǎn)外賣的宅男。
MessageQueue相當(dāng)于一個(gè)線上下單的餐廳,會(huì)根據(jù)顧客需求把所有訂單按出餐的時(shí)間排序(為什么不是下單時(shí)間,是因?yàn)橛行╊櫩蜁?huì)讓餐廳定時(shí)延后送過來)。
開始賞析外賣小哥與宅男之間的大戲吧
2、Looper
Looper 是用于為線程提供一個(gè)消息循環(huán)的機(jī)制。
線程默認(rèn)不提供Looper,而主線程是在應(yīng)用初始化的時(shí)候就為主線程提供了Looper. 證據(jù)何在呢?
餐廳默認(rèn)不提供外賣,也就沒有招聘外賣小哥。
當(dāng)系統(tǒng)啟動(dòng)一個(gè)應(yīng)用時(shí),入口函數(shù)即ActivityThread.main(String[] args)。
ActivityThread.java
public static void main(String[] args) {
...
Looper.prepareMainLooper();//接入外賣系統(tǒng),招聘外賣小哥。
...
Looper.loop(); // 系統(tǒng)運(yùn)轉(zhuǎn)
throw new RuntimeException("Main thread loop unexpectedly exited");
}
真像大白,嘖嘖。
主線程開了家餐廳,就同時(shí)做了外賣。率先吃了螃蟹,成為第一批富起來的資本家。
looper初始化:開餐廳的準(zhǔn)備工作
創(chuàng)建主線程的Looper
public static void prepareMainLooper() {
prepare(false); //
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
// 私有方法,參數(shù)為,是否允許該線程退出loop;公有方法prepare()默認(rèn)true。
// 這個(gè)參數(shù)會(huì)傳給MessageQueue
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
// 不要貪得無厭,一個(gè)老板只能開一家餐廳。
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
Looper.loop(); // 。外賣系統(tǒng)開啟。
進(jìn)入循環(huán),loop() :餐廳外賣系統(tǒng)開始運(yùn)轉(zhuǎn)
通過for(;;)循環(huán)的MessageQueue.next()讀取下一條消息,將消息分配給對(duì)應(yīng)的handler執(zhí)行。
讀取下一條消息可能會(huì)發(fā)生阻塞,一是消息隊(duì)列沒有消息,等待下一個(gè)消息入列;二是消息還未到執(zhí)行的時(shí)間(消息延遲執(zhí)行。)
外賣小哥送外一單外賣回來就繼續(xù)干下一單,取單時(shí)可能出現(xiàn),當(dāng)前沒人點(diǎn)單,或者還沒到送貨時(shí)間,于是就做家門口抽起了煙,等待資本家跟他說可以開始配送了。
// 源碼太長(zhǎng), 刪除一些,是系統(tǒng)關(guān)于每條消息執(zhí)行時(shí)間的一些log以及分析。
public static void loop() {
for(;;) {
Message msg = queue.next();
if (msg == null) { // 返回的消息為空,說明線程退出looper。
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg); // 配送
}
}
for(;;) 無限的取訂單,并且派送,直到老板明確告訴今天關(guān)門了不接單了。
退出循環(huán):餐廳今日休業(yè)
休業(yè)分兩種,第一種直接關(guān)閉外賣系統(tǒng)。第二種把超時(shí)的訂單送完再直接關(guān)閉外賣系統(tǒng)。
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
直接調(diào)用MessageQueue.quit(boolean) 方法,false,即刪除隊(duì)列中所有消息,
true則對(duì)比消息隊(duì)列中when與當(dāng)前的時(shí)間,msg.when小于當(dāng)前時(shí)間的依舊會(huì)被保留并執(zhí)行。比如:
handler.sendEmptyMessage(A)
handler.sendEmptyMessageDelay(B,1000)
looper.quitSafely()
則A會(huì)被執(zhí)行,B則被刪除。
3、Handler
發(fā)消息:下訂單
Handler中發(fā)消息有很多種方式。但殊途同歸,最終會(huì)走到sendMessageAtTime(msg,upTimeMillis)
//uptimeMillis 消息應(yīng)當(dāng)被執(zhí)行的時(shí)間。用于消息的排序。
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 記住 target
if (mAsynchronous) { // 讓我們也先記住這個(gè)變量。vip訂單。稍后再分析
msg.setAsynchronous(true);
}
// 進(jìn)入消息隊(duì)列。待分析MessageQueue再看
return queue.enqueueMessage(msg, uptimeMillis);
}
也就是每一個(gè)宅男腐女在同一家餐廳下的單,都會(huì)有下單時(shí)間或者定時(shí)送餐時(shí)間這個(gè)時(shí)間屬性
收消息: 配送過程
當(dāng)每一條消息從消息隊(duì)列取出來要被執(zhí)行時(shí),msg.target.dispatchMessage(msg), 上面的enqueueMessage()這個(gè)方法已經(jīng)告訴我們target就是相對(duì)應(yīng)的Handler!
也就是系統(tǒng)是知道每個(gè)訂單是對(duì)應(yīng)哪一個(gè)宅男的.然后根據(jù)指定的配送方式開始配送.
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) { // ①
handleCallback(msg); // 也就是執(zhí)行 message.callback.run();
} else {
if (mCallback != null) { // ② 怎么又有個(gè)CallBack...
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
聊一下這兩個(gè)CallBack吧。
第一個(gè)msg.callBack其實(shí)就是下面這種:
handler.post(Runnable {
//本質(zhì)依然是Message, Message還有個(gè) Runnable變量,系統(tǒng)直接執(zhí)行Runnable中代碼,無需設(shè)置what之類的
})
相當(dāng)于:
val msg = Message.obtain(handler, Runnable {...})
handler.sendMessage(msg)
msg.callBack 就是個(gè) Runnable
handler.mCallBack 就不一樣了。是Handler中定義的接口。
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
我們知道實(shí)例化Handler的普通方式是,Handler() 或者 Handler(Looper),然后實(shí)現(xiàn)handlerMessage()方法。
而實(shí)際上,在這兩種方法基礎(chǔ)上還可以添加 CallBack參數(shù),比如說、
Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//do sth by Message ...
return false;
}
});
這里的CallBack.handlerMessage就會(huì)優(yōu)先于Handler.handlerMessage()執(zhí)行。
如果CallBack.handlerMessage 返回true,則不再執(zhí)行Handler.handlerMessage()
說到構(gòu)造函數(shù),這里回頭來說下mAsynchronous這個(gè)變量。年少健忘的你,趕緊往上翻一下。
這個(gè)變量其實(shí)也是Handler構(gòu)造函數(shù)的一個(gè)參數(shù),默認(rèn)為false,用于表示這個(gè)Handler發(fā)出的消息是否為異步消息(true為異步),影響MessageQueue在出隊(duì)時(shí)的順序。
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
所以我們可以這么理解,收消息,是外賣小哥的配送過程,各種CallBack是宅男指定的配送方式(空運(yùn)啊,水運(yùn)或者瑪莎拉蒂專送)。
而異步消息,就是宅男是個(gè)Vip客戶,發(fā)出的每條訂單都是vip訂單,在特殊情況下會(huì)有優(yōu)先配送的特權(quán)。
而所謂的特殊情況,稍后分析。
移除消息:取消訂單
能移除指定的消息或者全部消息。
Handler實(shí)例 A,調(diào)用下述的移除消息的方法時(shí),只能移除 msg.target == A 的msg
也就是只能取消自己下的訂單。
removeMessages(int what) { // msg.what == what
mQueue.removeMessages(this, what, null); // MessageQueue的邏輯
}
removeMessages(int what, Object object) // msg.what == what && (msg.obj == object|| obj==null)
// token為null,則移除所有(msg.target == 此handler)的msg
removeCallbacksAndMessages(Object token)
4、MessageQueue
其實(shí)上述我們可以看到,Looper和Handler的部分邏輯都是在寫在MessageQueue里面的。
所以這里如果還提外賣小哥和宅男,就跟上面的描述重復(fù)了。我們就看看枯燥的source code吧。
MessageQueue,消息隊(duì)列。 存有當(dāng)前線程的所有未執(zhí)行的消息列表。這個(gè)列表以單鏈表形式存在。
鏈表的節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)正是 Message, Message有一個(gè) Message next = null變量用于指向下一個(gè)節(jié)點(diǎn)。
可通過Looper.myQueue()獲取當(dāng)前線程的MessageQueue實(shí)例。
MessageQueue提供了很多方法來對(duì)這個(gè)消息鏈表進(jìn)行操作。挑幾個(gè)重要的說一說。
也就是出列、入列以及退出循環(huán)。
出列
出列,即從消息隊(duì)列中讀取表頭的消息。Looper.loop()方法無限循環(huán)通過MessageQueue.next()獲取下一條消息。
next()內(nèi)部也有使用了for(;;)的無限循環(huán)迭代,直到鏈表為空的時(shí)候會(huì)阻塞直到下個(gè)消息的入列。
這里先拋出一個(gè)疑問,loop()已經(jīng)是無限循環(huán)了,為何這里也需要循環(huán)?
Message next() {
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// ①
//JNI 方法,當(dāng)前線程在nextPollTimeoutMillis 后再喚醒執(zhí)行
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) { // 注意這里Target==null
// Stalled by a barrier. Find the next asynchronous message in the queue.
// 找到第一個(gè)異步消息
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;
}
// ④
//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;
}
}
這個(gè)代碼有點(diǎn)長(zhǎng),主要分為四個(gè)步驟:
- 當(dāng)前線程進(jìn)入阻塞,指定nextPollTimeoutMillis時(shí)間之后被喚醒,-1表示一直阻塞(消息隊(duì)列為空)
- 如果消息列表的表頭的target為null,則獲取列表中的第一條異步消息。進(jìn)入步驟三。
- 如果當(dāng)前消息的執(zhí)行時(shí)間when 大于當(dāng)前時(shí)間,則nextPollTimeoutMilli等于兩者的差值,線程可能進(jìn)入阻塞狀態(tài),并進(jìn)入步驟四。否則next()方法直接返回當(dāng)前消息。
- 來到步驟四,要么是當(dāng)前消息列表為空,要么當(dāng)前要執(zhí)行的消息還沒到時(shí)間。也就是屬于空閑狀態(tài),所以會(huì)執(zhí)行IdleHandlers的任務(wù)。這里若IdleHandlers若為空,則進(jìn)入步驟1,否則執(zhí)行完IdleHandlers之后進(jìn)入步驟2.
從執(zhí)行步驟上來看,在步驟四上,是存在多次迭代甚至一直循環(huán)的可能性,所以這里用for(;;)來處理。就一般來說,for(;;)會(huì)很快在步驟三就退出循環(huán)
這里有兩個(gè)知識(shí)點(diǎn):IdleHandler 是什么,message.target什么時(shí)候回等于null。留著最后講解。
入列
根據(jù)handler.sendMessageXXX()調(diào)用的時(shí)間以及延遲的時(shí)間獲得執(zhí)行的時(shí)間,即msg.when,根據(jù)when,將message插在鏈表適當(dāng)?shù)奈恢?,可能是第一個(gè)。
boolean enqueueMessage(Message msg, long when) {
...
// 刪除一些判斷條件
...
synchronized (this) {
if (mQuitting) { // 執(zhí)行了 Looper.quit()
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; // mMessage,當(dāng)前消息鏈表的頭結(jié)點(diǎn)
boolean needWake;
if (p == null || when == 0 || when < p.when) { //當(dāng)前消息插在表頭
// 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;
}
退出循環(huán)
在Looper.quit()我們就提到,其內(nèi)部實(shí)現(xiàn)調(diào)用的是 MessageQueue.quit(boolean)
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
// 把
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
5、小結(jié)
線程是餐廳,Looper該餐廳的是外賣小哥,Handler是某個(gè)點(diǎn)外賣的宅男。
MessageQueue相當(dāng)于一個(gè)線上下單的餐廳,會(huì)根據(jù)顧客需求把所有訂單按出餐的時(shí)間排序(為什么不是下單時(shí)間,是因?yàn)橛行╊櫩蜁?huì)讓你定時(shí)送過來)。
外賣小哥負(fù)責(zé)按時(shí)把外賣按照指定的配送方式送到宅男手上。則宅男可以下單、指定配送方式以及取消訂單。
而IdelHandler就像沒人下單,老板很閑,就去接了點(diǎn)私活。
異步消息呢,就像是Vip訂單,當(dāng)出現(xiàn)特殊情況(target
==null),我們稱之為會(huì)員活動(dòng)日,當(dāng)會(huì)員活動(dòng)日到來的時(shí)候,外賣小哥就會(huì)優(yōu)先派送Vip訂單,除非已經(jīng)沒有Vip訂單了或者活動(dòng)結(jié)束了才會(huì)派送普通訂單。
當(dāng)老板想停止?fàn)I業(yè)了,可以發(fā)出兩種指令,一種就是把所有的剩下的訂單全部丟掉直接關(guān)門,還有一種就是由于訂餐量太多,很多超時(shí)沒能派送出去的訂單,老板會(huì)把派完再關(guān)門,至于那些還沒到時(shí)間派送的訂單,就只能全部自動(dòng)退訂!
6、異步消息與同步消息:會(huì)員活動(dòng)日
前面我們已經(jīng)有解釋過同步異步消息了,就是在Handle對(duì)象初始化時(shí)構(gòu)造參數(shù)boolean async的區(qū)別。
會(huì)員活動(dòng)日的到來
一般來說這個(gè)參數(shù)不會(huì)有任何作用,直到會(huì)員活動(dòng)日的到來。
也就是餐廳老板,在門口掛上牌子,MessageQueue.postSyncBarrier()
MessaegQueue.java
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
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;
}
}
根據(jù)需求在指定的時(shí)間when,宣布會(huì)員活動(dòng)的到來。即插入一條target==null 的message!
同時(shí)返回一個(gè) token值,即 本次是 第幾屆 會(huì)員活動(dòng)日。
會(huì)員活動(dòng)日的結(jié)束。
如果會(huì)員活動(dòng)一直不結(jié)束的話,每送完一單,外賣小哥都是會(huì)先看看還有沒Vip單,然后優(yōu)先派送Vip,如果這個(gè)訂單還沒有到時(shí)間,那么外賣小哥就會(huì)先停下來抽個(gè)煙。。直到可以派送Vip單。。這樣效率就很低了。所以,需要及時(shí)的取消活動(dòng)日。
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
根據(jù)token 結(jié)束對(duì)應(yīng)的該屆的活動(dòng)。
7、IdleHandler :接私活
雖然不在飯點(diǎn),不用送外賣,作為老板自然要有點(diǎn)上進(jìn)心,引進(jìn)點(diǎn)其他業(yè)務(wù)坐也是提升營(yíng)收的好辦法嘛。比如既然大家平常下訂單都喜歡備注多加米飯,趁現(xiàn)在多煮一鍋飯嘛。或者直接跟別人三缺一,打個(gè)四圈,也是勞逸結(jié)合嘛。
在講述MessageQueue.next()已經(jīng)提過源碼了,再把相關(guān)的貼一下吧。
public Message next() {
int pendingIdleHandlerCount = -1;
for(;;) {
...// msg == null
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) { // 注意結(jié)合代碼塊最后面的注釋
// 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);
}
}
}
// 設(shè)置為0之后,在這個(gè)for(;;)里面或者這次的next()的調(diào)用中,上面的這個(gè)idle的for循環(huán)不會(huì)再次運(yùn)行了
pendingIdleHandlerCount = 0;
}
只有在 當(dāng) 消息隊(duì)列為空或者 第一條消息還沒到執(zhí)行的時(shí)間,IdleHandler才會(huì)被執(zhí)行,跟普通的Message不同的是,每條IdleHandler可能會(huì)被執(zhí)行多次,如果這個(gè)IdlerHandler被定義為保留的話-- keep = idler.queueIdle() == true
當(dāng)然在同一次空閑時(shí)間內(nèi),Idles只會(huì)被執(zhí)行一次。
那么如何接私活呢?
先看下私活模板長(zhǎng)啥樣。。
MessageQueue.java
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();
}
私活的內(nèi)容寫在queueIdle()里面,同時(shí)用返回值告知老板,這個(gè)是不是長(zhǎng)期任務(wù)。
然后通過訂單系統(tǒng)MessageQueue.addIldeHandler()把私活加入到私活列表上!
MessageQueue.java
/**
* Add a new {@link IdleHandler} to this message queue. This may be
* removed automatically for you by returning false from
* {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
* invoked, or explicitly removing it with {@link #removeIdleHandler}.
*
* <p>This method is safe to call from any thread.
*
* @param handler The IdleHandler to be added.
*/
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
后記
好啦到這里尾聲了。修修改改,偷偷懶懶。我太難了。。找時(shí)間再把,同步消息以及異步消息的例子給補(bǔ)上。