Android的消息傳遞,是系統(tǒng)的核心功能,對(duì)于如何使用相信大家都已經(jīng)相當(dāng)熟悉了,這里簡(jiǎn)單提一句。我們可以粗糙的認(rèn)為消息機(jī)制中關(guān)鍵的幾個(gè)類的功能如下:
Handler:消息處理者
Looper:消息調(diào)度者
MessageQueue:存放消息的地方
使用過(guò)程:
Looper.prepare > #$%^^& > Looper.loop(死循環(huán)) --- loop到一個(gè)消息 > Handler處理
好了,我們直接看源碼吧。
Java層
消息機(jī)制是伴隨線程的,也就是說(shuō)上面的幾個(gè)類在可以在任何一個(gè)線程中都有實(shí)例的。
先看Looper吧。以主線程為例,Android進(jìn)程在初始化,會(huì)調(diào)用prepareMainLooper
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
...
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
...
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
以上幾個(gè)方法就是Looper初始化,如果是主線程Looper會(huì)創(chuàng)建一個(gè)不可退出的MessageQueue,并把looper實(shí)例放入線程獨(dú)立(ThreadLocal)變量中。
Looper#loop
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
...
try {
msg.target.dispatchMessage(msg);
}
...
msg.recycleUnchecked();
}
}
Looper prepare后就可以loop了,loop非常簡(jiǎn)單,一直去queue中拿消息就好了,拿到了交給target也就是Handler處理。大家有可能會(huì)奇怪這種死循環(huán),執(zhí)行起來(lái)不會(huì)太sb粗暴了嗎?其實(shí)這個(gè)解決方式在queue.next!?。『竺嬖僦v。
Handler#dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
handler收到后,如果發(fā)現(xiàn)message的callback不為空,則只處理callback。(提一句,我們用的很多的handler.post(Runnable),其實(shí)這個(gè)Runnable就是這里的callback,也就是說(shuō)post的Runnable實(shí)質(zhì)上是一個(gè)優(yōu)先級(jí)很高的Message),如果沒(méi)有則嘗試交給handler本身的callback處理(handler初始化的時(shí)候可以用callback方式構(gòu)造),再?zèng)]有才到我們常用的handleMessage方法,這里就是我們經(jīng)常重寫的方法。
再說(shuō)說(shuō)消息的發(fā)送,一般handler會(huì)調(diào)用sendMessage方法,但是最終這個(gè)方法還是會(huì)跑到這里
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
再交給MessageQueue
boolean enqueueMessage(Message msg, long when) {
。。。
synchronized (this) {
。。。
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
。。。
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
if (needWake){
nativeWake(mPtr);
}
}
return true;
}
MessageQueue會(huì)把消息插入隊(duì)列,并依次改變隊(duì)列中各個(gè)消息的指針。
咦,好像只用Java層貌似就能把整個(gè)消息機(jī)制說(shuō)通了,native代碼在哪兒?有何用呢?
但是,剛才提到了Looper初始化的時(shí)候也會(huì)新建一個(gè)MessageQueue
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
好了,我們第一個(gè)native方法出來(lái)了。這時(shí)候我們可以猜得到,MessageQueue才是整個(gè)消息機(jī)制的核心!
Native層
接上面Java層的代碼,MessageQueue構(gòu)造的時(shí)候會(huì)調(diào)一個(gè)nativeInit。
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
。。。
}
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
native層調(diào)用init方法后,會(huì)在native層構(gòu)建一個(gè)native Looper!來(lái)看看native Looper的初始化
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
...
rebuildEpollLocked();
}
這里創(chuàng)建了一個(gè)eventfd,代碼來(lái)自最新的8.0,這部分和5.0 pipe管道的mWakeReadPipeFd和mWakeWritePipeFd稍微有點(diǎn)不一樣,前者是等待/響應(yīng),后者是讀取/寫入。只是android選取方式的不同而已,這塊就不細(xì)說(shuō)。
void Looper::rebuildEpollLocked() {
。。。
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event));
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
。。。
}
再到rebuildEpollLocked這個(gè)方法中,可以看到通過(guò)epoll_create創(chuàng)建了一個(gè)epoll專用的文件描述符,EPOLL_SIZE_HINT表示mEpollFd上能監(jiān)控的最大文件描述符數(shù)。最后調(diào)用epoll_ctl監(jiān)控mWakeEventFd文件描述符的Epoll事件,即當(dāng)mWakeEventFd中有內(nèi)容可讀時(shí),就喚醒當(dāng)前正在等待的線程.。
這里不了解的人可能聽(tīng)著暈,上面這么一大段一句話概括就是:Android native層用了Epoll模型。什么是Epoll模型呢?我先簡(jiǎn)單介紹一下。
Epoll(必看?。。。?br> 為什么要引入呢?
在Looper.loop的時(shí)候提到了,android不會(huì)簡(jiǎn)單粗暴地真的執(zhí)行啥都沒(méi)干的死循環(huán)。剛才說(shuō)了,問(wèn)題出在queue.next。Epoll干的事就是: 如果你的queue中沒(méi)有消息可執(zhí)行了,好了你可以歇著了,等有消息的我再告訴你。這個(gè)queue.next就是“阻塞”(休眠)在這里。
Epoll簡(jiǎn)單介紹
1、傳統(tǒng)的阻塞型I/O(一邊寫,一邊讀),一個(gè)線程只能處理一個(gè)一個(gè)IO流。
2、如果一個(gè)線程想要處理多個(gè)流,可以采用了非阻塞、輪詢I/O方式,但是傳統(tǒng)的非阻塞處理多個(gè)流的時(shí)候,會(huì)遍歷所有流,但是如果所有流都沒(méi)數(shù)據(jù),就會(huì)白白浪費(fèi)CPU。
。。。
于是出現(xiàn)了select和epoll兩種常見(jiàn)的代理方式。
3、select就是那種無(wú)差別輪詢的代理方式。epoll可以理解為Event poll,也就是說(shuō)代理者會(huì)代理流的時(shí)候也伴隨著事件,因此有了對(duì)應(yīng)事件,就可以避免無(wú)差別輪詢了。
4、其通常的操作有:epoll_create(創(chuàng)建一個(gè)epoll)、epoll_ctl(往epoll中增加/刪除某一個(gè)流的某一個(gè)事件)、epoll_wait(在一定時(shí)間內(nèi)等待事件的發(fā)生)
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
好了,我們結(jié)合Looper初始化的代碼來(lái)讀一下epoll在這里干了什么吧。
我直接翻譯了:往mEpollFd代理中、注冊(cè)、一個(gè)叫mWakeEventFd流、的數(shù)據(jù)流入事件(EPOLLIN)
這樣大家應(yīng)該懂了吧。。。
接上MessageQueue在初始化后,在native創(chuàng)建了一個(gè)Looper。
我們繼續(xù)消息的發(fā)送和提取在native層的表現(xiàn)。其實(shí)native層主要負(fù)責(zé)的是消息的調(diào)度,比如說(shuō)何時(shí)阻塞、何時(shí)喚醒線程,避免CPU浪費(fèi)。
native發(fā)送
發(fā)送在native比較簡(jiǎn)單,handler發(fā)送消息后,會(huì)到MessageQueue的enqueueMessage,此時(shí)在線程阻塞的情況下,會(huì)調(diào)用nativeWake來(lái)喚起線程。
void NativeMessageQueue::wake() {
mLooper->wake();
}
void Looper::wake() {
。。。
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
。。。。
}
}
這里TEMP_FAILURE_RETRY是一個(gè)宏定義,顧名思義,就是不斷地嘗試往mWakeEventFd流里面寫一個(gè)無(wú)用數(shù)據(jù)直到成功,以此來(lái)喚醒queue.next。這部分就不多說(shuō)了。
native消息提取
也就是queue.next
Message next() {
。。。
for (;;) {
。。。
nativePollOnce(ptr, nextPollTimeoutMillis);
。。。
}
}
可以看到,又是一個(gè)死循環(huán)(阻塞)。繼續(xù)往下看
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
。。。
mLooper->pollOnce(timeoutMillis);
。。。
}
mLooper->pollOnce
mLooper->pollInner
int Looper::pollInner(int timeoutMillis) {
。。。
int result = POLL_WAKE;
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
mPolling = false;
mLock.lock();
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
}
。。。
}
。。。
mLock.unlock();
。。。
return result;
}
在這里,我們注意到epoll_wait方法,這里會(huì)得到一段時(shí)間內(nèi)(結(jié)合消息計(jì)算得來(lái)的)收到的事件個(gè)數(shù),這里對(duì)于queue來(lái)說(shuō)就是空閑(阻塞)狀態(tài)。過(guò)了這個(gè)時(shí)間后,看看事件數(shù),如果為0,則意味著超時(shí)。否則,遍歷所有的事件,看看有沒(méi)有mWakeEventFd,且是EPOLLIN事件的,有的話就真正喚醒線程、解除空閑狀態(tài)。
消息機(jī)制在native層的主要表現(xiàn)就是這些。
想要安卓高級(jí)視頻學(xué)習(xí)資料的加群:4112676