一遗遵、消息模型
生產(chǎn)者-消費(fèi)者模型,Android 架構(gòu)逸嘀,線程間通信车要,基于消息機(jī)制,消費(fèi)者從隊(duì)列獲取崭倘、處理消息翼岁,實(shí)現(xiàn)休眠與喚醒,生產(chǎn)者向隊(duì)列插入消息司光,通知消費(fèi)者琅坡。
ActivityThread 類 main() 方法,主線程創(chuàng)建 Looper 和 Queue残家,Looper.loop() 方法榆俺,啟動循環(huán),主線程 main() 方法未結(jié)束坞淮,(運(yùn)行+休眠)茴晋,否則拋出異常退出。
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
隊(duì)列是空時回窘,消費(fèi)者線程休眠诺擅,防止 CPU 空跑占用資源,不空時毫玖,消費(fèi)者被喚醒掀虎,生產(chǎn)者+消費(fèi)者,兩個線程 + 阻塞隊(duì)列 BlockQueue 可實(shí)現(xiàn)付枫,利用 Java 中 Condition 的 await 和 signal 機(jī)制烹玉。
Android,采用 Native 層 epoll 方案阐滩。
Handler 類,消息構(gòu)建掂榔、發(fā)送继效、回調(diào)症杏。
Message 類,傳遞消息對象瑞信。
MessageQueue 類厉颤,隊(duì)列,控制消息吞吐凡简。
Looper 類逼友,線程循環(huán) loop。
構(gòu)建線程的消息處理機(jī)制秤涩,在線程的 run() 方法啟動 Looper 類的 prepare() 方法帜乞,準(zhǔn)備一個線程本地的 Looper 實(shí)例。
ThreadLocal 類對 Looper 對象進(jìn)行線程隔離筐眷。
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));
}
Looper 類構(gòu)造方法黎烈,創(chuàng)建消息隊(duì)列,MessageQueue 類構(gòu)造方法匀谣,初始化 Native 層隊(duì)列照棋。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
Handler 類構(gòu)造方法,支持 綁定特定線程 Looper (入?yún)?振定,默認(rèn) Handler 對象創(chuàng)建線程 Looper必怜,
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
//mLooper是空會拋出異常
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler 綁定的 Looper,決定它向哪一個 MQ 發(fā)送消息后频。
Native 層 NativeMessageQueue 類梳庆,Looper 類,控制線程休眠和喚醒卑惜。
二膏执、工作過程
Handler 類,sendMessage() 方法露久,將消息插入 MessageQueue更米。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
將消息 target 設(shè)置成調(diào)用者 Handler 對象,每個消息都引用處理它的回調(diào)類毫痕。任何線程都可以將消息插入隊(duì)列征峦,MessageQueue 類的 enqueueMessage(),方法內(nèi)部 synchronized 代碼同步消请。
消費(fèi)者線程 Looper 類栏笆,loop()方法,循環(huán)遍歷臊泰。
public static void loop() {
//當(dāng)前線程Looper
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
//循環(huán)
for (;;) {
Message msg = queue.next(); // 這里可能休眠
if (msg == null) {
// 沒有消息蛉加,代表消息隊(duì)列已經(jīng)退出,結(jié)束循環(huán)。
return;
}
//Message消息處理
try {
msg.target.dispatchMessage(msg);
} finally {
}
...
}
}
通過隊(duì)列 next() 方法针饥,獲得消息厂抽,消息 target (發(fā)送者 Handler )回調(diào),如果隊(duì)列是空丁眼,進(jìn)入休眠狀態(tài)筷凤。
Handler 類 dispatchMessage() 方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
處理優(yōu)先級户盯。
消息體 Callback > Handler 類內(nèi)部 Callback > 重寫的 handleMessage() 方法嵌施。
三饲化、底層原理
消息機(jī)制核心原理在 Native 層莽鸭。
1,Looper吃靠,綁定隊(duì)列硫眨,消息隊(duì)列 MessageQueue,next() 方法巢块,查詢消息礁阁、進(jìn)入底層休眠。
Message next() {
final long ptr = mPtr;
//底層消息隊(duì)列已經(jīng)銷毀族奢,直接退出循環(huán)姥闭。
//Looper.loop收到null時,也將退出for循環(huán)越走,結(jié)束線程棚品。
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;//第一次進(jìn)入時,不設(shè)置休眠等待時間廊敌。
for (;;) {
//JNI方法铜跑,底層休眠,第一次循環(huán)休眠時間是0骡澈。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 如果隊(duì)列第一個是同步柵欄消息锅纺,則跳過后續(xù)的同步消息,直接找到異步消息執(zhí)行肋殴,確保異步消息的優(yōu)先級高囤锉。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//若不是同步柵欄,msg就是隊(duì)列的第一個消息护锤。
if (msg != null) {
if (now < msg.when) {
// 發(fā)現(xiàn)此時還未到消息的執(zhí)行時間官地,設(shè)置差值,將繼續(xù)循環(huán)蔽豺,休眠差值時間区丑。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 此刻已經(jīng)到達(dá)消息設(shè)定執(zhí)行時間,消息返回給loop方法。
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 若消息隊(duì)列已經(jīng)空了沧侥,設(shè)置無限等待休眠可霎,直到手動喚醒,在插入時宴杀。
nextPollTimeoutMillis = -1;
}
...
}
...
}
}
nativePollOnce() 方法癣朗,Native 層消息隊(duì)列指針 mPtr,根據(jù)當(dāng)前時間和隊(duì)列內(nèi)消息 delay 執(zhí)行的目標(biāo)時間旺罢,計(jì)算休眠時間 nextPollTimeoutMillis旷余,通知底層。
喚醒時扁达,從隊(duì)列 get 消息正卧,返回 Looper 類 loop() 處理。
nativePollOnce() 通知 Native 層 Looper跪解,調(diào)用 Looper 類 pollInner() 方法炉旷。
int Looper::pollInner(int timeoutMillis) {
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
//休眠
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//遍歷事件數(shù)量
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
//若是mWakeEventFd句柄發(fā)生的事件,如向其寫入了數(shù)據(jù)叉讥。
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken();
}
}
}
Done: ;
// 處理mMessageEnvelopes中的message窘行,拿到handler。
// 這里面的消息是在底層發(fā)送的图仓,底層Looper#sendMessage方法罐盔。
mNextMessageUptime = LLONG_MAX;
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
{
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
//執(zhí)行MessageHandler的handleMessage消息。
handler->handleMessage(message);
}
mLock.lock();
mSendingMessage = false;
result = POLL_CALLBACK;
} else {
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
mLock.unlock();
return result;
}
利用 epoll_wait() 方法救崔,實(shí)現(xiàn)線程休眠惶看,參數(shù) mEpollFd 是 epoll 句柄,由 epoll_create() 方法創(chuàng)建帚豪,參數(shù) eventItems碳竟,監(jiān)聽事件的集合,timeoutMillis 休眠時間狸臣。
該 timeout 由上層計(jì)算莹桅,超時自動喚醒,回到上層消息處理烛亦。
描述符 mWakeEventFd诈泼,監(jiān)聽注冊流的事件,(非 timeout )煤禽,Java 層插入新消息時寫入該事件铐达。
Java 層隊(duì)列是空時,不設(shè)定 timeout檬果,timeoutMillis 值-1瓮孙,epoll_wait() 方法一直休眠唐断。
舉例,Java 層消息隊(duì)列第一個消息的執(zhí)行時間 when 超過當(dāng)前時間1分鐘杭抠,表示消息延遲1分鐘執(zhí)行脸甘,通過 nativePollOnce() 方法,通知 Native 層 Looper 在 epoll_wait() 方法位置休眠1分鐘偏灿。
Native 層丹诀,Looper 類的 pollInner() 方法提供給上層調(diào)用,(Native 層需要利用 Looper 機(jī)制的地方也會調(diào)用翁垂,在Done代碼塊铆遭,處理 Native 層 Looper 類 sendMessage() 方法發(fā)送的消息,MessageHandler 類負(fù)責(zé)回調(diào))沿猜。
2枚荣,Handler 類,調(diào)用隊(duì)列的 enqueueMessage() 方法邢疙,插入消息棍弄。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//消息隊(duì)列是空,或立即執(zhí)行疟游,或按時間排序插入頭部。設(shè)置喚醒標(biāo)志痕支。
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 {
//若第一個消息是同步柵欄(即沒有派發(fā)目標(biāo))颁虐,且插入消息是異步消息。
//不管怎樣卧须,都需要喚醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//隊(duì)里已經(jīng)有異步消息啦另绩,不需要喚醒。
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
當(dāng)隊(duì)列是空或 when 是0花嘶,或執(zhí)行時間 when 小于頭部消息時間笋籽,插入到頭部位置,根據(jù)時間先后排序椭员,設(shè)置喚醒標(biāo)志车海。
可以喚醒時,代表有立即處理的消息隘击。
當(dāng)不滿足上述條件時侍芝,如果存在同步柵欄,個人理解是遇到 SyncBarrier 這個消息埋同,優(yōu)先處理后續(xù)的異步消息州叠,屏蔽同步消息,SyncBarrier 消息沒有 target 目標(biāo)凶赁,在 Android Framework 中是 hide 狀態(tài)咧栗,不會向 App 暴露逆甜。因此,上層應(yīng)用無此類消息致板,從源碼可知忆绰,當(dāng)隊(duì)列頭部遇到 SyncBarrier 消息,且插入消息是異步可岂,會主動喚醒错敢,除非隊(duì)列中已經(jīng)存在其他異步消息。
根據(jù)喚醒標(biāo)志缕粹,nativeWake() 方法稚茅,若不喚醒,只將消息插入到隊(duì)列合適位置平斩,線程保持休眠亚享。
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
Native 層篙程,wake() 方法智嚷,向 mWakeEventFd 句柄寫入內(nèi)容砰奕,(消息插入可能發(fā)生在其他任何線程)玻蝌。
Looper.loop() 消費(fèi)者線程在 MQ.next() 方法 epoll_wait() 位置休眠样悟,監(jiān)聽到 mWakeEventFd 事件仁讨,即注冊的流發(fā)生了事件绽乔,(寫入內(nèi)容非重點(diǎn))复哆,喚醒瘦馍。
awoken() 方法歼秽,數(shù)據(jù)流讀取,(內(nèi)容非重點(diǎn))情组,關(guān)鍵是線程被喚醒燥筷,繼續(xù)執(zhí)行,從 Native 層回到 Java 層 MQ.next() 位置 查找消息院崇。
四肆氓、總結(jié)擴(kuò)展
1,Native 層 Looper 類底瓣,利用 epoll 提供線程休眠和喚醒機(jī)制谢揪。
2,自動喚醒濒持,根據(jù)上層隊(duì)列對消息處理時間 when 的判斷键耕,決策喚醒 timeout 時間。
3柑营,主動喚醒屈雄,由 Handler 類主導(dǎo),消息隊(duì)列決策官套,向 epoll 監(jiān)聽的文件描述符寫入字段酒奶,觸發(fā)流事件蚁孔,即可喚醒。
3惋嚎,隊(duì)列按照消息處理時間升序排列杠氢。
4,Java 層功能另伍,消息創(chuàng)建鼻百、發(fā)送,隊(duì)列維護(hù)摆尝,喚醒時機(jī)温艇,消息處理。
擴(kuò)展堕汞,epoll 機(jī)制有兩種模式勺爱,ET 邊緣模式和 LT 水平模式 (默認(rèn))。
ET 模式讯检,狀態(tài)發(fā)生變化時才會事件通知琐鲁,例如,向流中寫入 10 個字節(jié)人灼,線程喚醒围段,僅讀取5個字節(jié),再次循環(huán)到此處時挡毅,不會再收到通知蒜撮,除非再次寫入句柄數(shù)據(jù),改變狀態(tài)跪呈。
LT 模式,有數(shù)據(jù)留在 Buffer 未讀取取逾,每次循環(huán)到此位置時耗绿,一直收到事件通知。
任重而道遠(yuǎn)