消息分發(fā)和處理是如何工作的仁热?
消息處理框架如何創(chuàng)建和啟動
1.創(chuàng)建消息循環(huán)分發(fā)對象Looper
private Looper(boolean quitAllowed) {
//消息儲存的隊列
mQueue = new MessageQueue(quitAllowed);
//創(chuàng)建消息處理框架的線程
mThread = Thread.currentThread();
}
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));
}
//開啟消息循環(huán)
public static void loop() {
final Looper me = myLooper();
//檢查當前線程是否已經(jīng)創(chuàng)建了Looper對象并緩存
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//當前l(fā)ooper需要分發(fā)的消息的消息隊列
boolean slowDeliveryDetected = false;//日志開關
//注意:死循環(huán)遍歷
for (;;) {
Message msg = queue.next(); // 獲取下一個消息 => 可能會阻塞(比如消息隊列為空時,后續(xù)分析該源碼)但是阻塞會釋放cpu,這是為什么死循環(huán)遍歷卻不會導致主線程卡住的原因
if (msg == null) {
//這里標示著消息分發(fā)流程已經(jīng)終止了
return;
}
// 獲取當前日志打印服務提供者,Looper允許外部設置自己的打印服務棵帽,比如主線程就在ActivityThread中設置了LogPrinter對象,一些卡頓檢測框架就是根據(jù)消息處理前后的日志中的時間來統(tǒng)計耗時的
final Printer logging = me.mLogging;
if (logging != null) {
//打印當前消息開始分發(fā)和處理的時間
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...//Trace對象來跟蹤處理時間
try {
//將消息分發(fā)給其目標處理者 => 消息不能沒有目標處理者
msg.target.dispatchMessage(msg);//消息處理流程后續(xù)分析
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
//打印消息處理結(jié)束日志
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//回收該消息:將該消息加入對象池 并重置其狀態(tài)
msg.recycleUnchecked();
}
}
- Looper只能通過prepare及其重載方法創(chuàng)建频鉴,且同一線程中只能創(chuàng)建一次(即調(diào)用prepare方法一次)榄檬,創(chuàng)建后存儲在ThreadLocal中
- Looper對象創(chuàng)建時必須創(chuàng)建MessageQueue隊列,可理解為將消息分發(fā)者和消息來源(消息隊列)建立聯(lián)系
- 消息循環(huán)是死循環(huán)遍歷刹悴,其不卡住的原因是消息隊列的next方法雖然阻塞行楞,但是會釋放cpu
擴展:ThreadLocal的set方法是如何工作的?
public void set(T value) {
//獲取此方法被調(diào)用時所在的線程
Thread t = Thread.currentThread();
//獲取Thread對象的threadLocals變量 類型是ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//將當前value保存到map中土匀,key為當前的ThreadLocal子房,即:ThreadLocalMap對象可以保存多個value,但同一個ThreadLocal對象就轧,只會有一個value被保存
map.set(this, value);
else
//如果map為空 則會創(chuàng)建一個ThreadLocalMap對象并設置初始值
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2.創(chuàng)建消息處理者
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;
}
1.創(chuàng)建Handler對象時证杭,將消息分發(fā)對象、消息隊列等復制給處理者妒御,這里callback在消息處理時會用到解愤,mAsynchronous表示是否為異步,后面解釋異步消息原理時用到
消息如何創(chuàng)建和發(fā)送給消息框架
創(chuàng)建一個空消息
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 static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // 清空消息的狀態(tài)
sPoolSize--;
return m;
}
}
return new Message();
}
1.Message對象可以直接創(chuàng)建或者從對象緩存池中獲取
發(fā)送消息給消息隊列
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//發(fā)送一個在將來時間點uptimeMillis處執(zhí)行的消息
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) {
//消息的目標設置當前handler
msg.target = this;
if (mAsynchronous) {
//如果handler是異步handler乎莉,則其發(fā)送的消息都是異步消息 => 異步消息在后續(xù)分析
msg.setAsynchronous(true);
}
//將消息添加到消息隊列
return queue.enqueueMessage(msg, uptimeMillis);
}
boolean enqueueMessage(Message msg, long when) {
//先檢查消息狀態(tài):即 有沒有處理者和是否已經(jīng)在使用了
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) {
//如果消息處理正在停止送讲,則不發(fā)送該消息
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;
}
//設置消息狀態(tài):在使用 和 時間點
msg.markInUse();
msg.when = when;
Message p = mMessages;//消息頭指針=>消息隊列由鏈表實現(xiàn) 其最大容量是多少呢奸笤?
boolean needWake;//是否需要喚醒正在消息隊列上等待的對象
if (p == null || when == 0 || when < p.when) {
// 插入到隊列頭部
msg.next = p;
mMessages = msg;
needWake = mBlocked;//mBlocked表示是否在消息分發(fā)時阻塞 =>如果之前l(fā)ooper在此隊列上阻塞,則需要喚醒
} else {
//如果之前已在等待 并且 隊列非空哼鬓,頭節(jié)點沒有處理者 且 當前插入消息為異步监右,則需要喚醒等待
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;
}
//如果需要喚醒等待者异希,則通過native的方法喚醒秸侣,這里mPtr為native層消息隊列的地址
if (needWake) {
//實際上,native層是通過向管道寫入某個字符來喚醒管道另一端的等待者宠互,
nativeWake(mPtr);//這里調(diào)用后味榛,會喚醒在MessageQueue的next方法上等待的Looper,
}
}
return true;
}
1.消息隊列由鏈表實現(xiàn)予跌,其容量無限制
消息框架如何分發(fā)和處理消息
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
- 如果消息自己攜帶了callback(Runnable類型)搏色,則直接調(diào)用callback的run方法 否則到第2步
- 如果Handler中mCallback不為空,則調(diào)用mCallback的handleMessage方法處理 如果其放回true券册,則終止后續(xù)流程
- 直接調(diào)用Handler自身的handleMessage方法處理消息
Handler是如何切換線程的
Handler是用于同一個進程的線程間通信频轿。Looper讓主線程無限循環(huán)地從自己的MessageQueue拿出消息處理,所以我們就可以知道處理消息肯定是在主線程中處理的烁焙,那么是怎樣在其他的線程往主線程的隊列里放入消息呢航邢?道理其實很簡單,我們知道在同一進程中線程和線程之間資源是共享的骄蝇,也就是對于任何變量在任何線程都是可以訪問和修改的膳殷,只要考慮并發(fā)性做好同步就行了,那么只要拿到MessageQueue 的實例九火,就可以往主線程的MessageQueue放入消息赚窃,主線程在輪詢的時候就可以在主線程處理這個消息。那么是怎么拿到主線程 MessageQueue的實例呢岔激,當然是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Google 為了統(tǒng)一添加消息和消息的回調(diào)處理勒极,又專門構(gòu)建了Handler類,你只要在主線程構(gòu)建Handler類虑鼎,那么這個Handler實例就獲取主線程MessageQueue實例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;)辱匿,Handler 在sendMessage的時候就通過這個引用往消息隊列里插入新消息。Handler 的另外一個作用炫彩,就是能統(tǒng)一處理消息的回調(diào)匾七。這樣一個Handler發(fā)出消息又確保消息處理也是自己來做,這樣的設計非常的贊媒楼。具體做法就是在隊列里面的Message持有Handler的引用(哪個handler 把它放到隊列里乐尊,message就持有了這個handler的引用)戚丸,然后等到主線程輪詢到這個message的時候划址,就來回調(diào)我們經(jīng)常重寫的Handler的handleMessage(Message msg)方法扔嵌。
空閑消息是如何工作的?及其實際應用
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
//表示消息處理已經(jīng)停止
return null;
}
int pendingIdleHandlerCount = -1; // 需要空閑消息數(shù)量
int nextPollTimeoutMillis = 0;//poll消息需等待的時間 此處只有native層會使用該值夺颤,對native而言 其值有多重含義:0:直接返回 -1:無限等待 其他值:等待時常 [詳情](https://www.kancloud.cn/alex_wsc/android-deep2/413394)
//死循環(huán)遍歷
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//通知native層消費其消息痢缎,該方法會阻塞,其是當前方法阻塞的原因
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;
//如果頭節(jié)點消息沒有target 這種情況是由于給消息隊列設置了異步柵欄
if (msg != null && msg.target == null) {
//找到第一個異步消息世澜,此處是為了柵欄設計
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 下一個消息還沒有準備好 所以設置一個下一次喚醒的延時時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//找到了一個msg,將消息從隊列中移除
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
//直接返回該消息
return msg;
}
} else {
// 沒有更多消息 nextPollTimeoutMillis = -1時 nativePollOnce方法會無限阻塞
nextPollTimeoutMillis = -1;
}
// 正在退出
if (mQuitting) {
dispose();
return null;
}
//pendingIdleHandlerCount只可能在第一次賦值時時小于0
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
/此時沒有消息需要處理:即空閑狀態(tài)
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 沒有需要在空閑狀態(tài)下執(zhí)行的任務 則標記進入阻塞狀態(tài)
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//拷貝空閑時任務 數(shù)組
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//處理空暇時任務
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // 釋放保存的空閑時任務數(shù)組
boolean keep = false;//是否需要保存該任務
try {
keep = idler.queueIdle();//如果這里返回了true 即保持該任務 那么該任務下次空閑時會再被執(zhí)行
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
//如果不需要保持 則會移除該任務
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 重置空閑時任務數(shù)量
pendingIdleHandlerCount = 0;
//設置等待時間為0 防止在處理空閑任務時添加了新的任務
nextPollTimeoutMillis = 0;
}
}
柵欄設計原理及其應用独旷?
柵欄的作用:添加柵欄后寥裂,消息隊列的next不會返回同步消息嵌洼,只會返回異步消息封恰。又因為消息默認時同步的,所以此時Looper線程將阻塞業(yè)務消息的分發(fā)和處理诺舔。待移除柵欄后鳖昌,才會分發(fā)和處理。
柵欄的應用:ViewRootImpl請求垂直同步信號時许昨,添加一個柵欄給主線程的隊列,之后業(yè)務方post的消息都不會執(zhí)行糕档,直到處置同步回調(diào)時移除了柵欄
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
//向消息隊列中添加一個異步柵欄 返回值為該柵欄的token 移除柵欄需要使用到token
private int postSyncBarrier(long when) {
//這里添加柵欄的目的是 攔住同步消息的分發(fā)和處理 所以不用去喚醒隊列
synchronized (this) {
final int token = mNextBarrierToken++;//柵欄的token 標示柵欄唯一性
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
//將消息添加到隊列中 需要注意 這里的消息沒有target 即沒有處理者 如果記得文章前面的next方法的實現(xiàn),就會知道柵欄是怎么攔截同步消息的
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;
}
}
最后附上類圖