handler要想能工作起來流昏,第一步要做的事情是讓線程變?yōu)椤坝绖訖C(jī)”,也就是讓線程一直循環(huán)起來,不死掉亏掀,這樣線程就可以不斷的處理各種任務(wù)了。那這節(jié)就來介紹下如何讓線程變?yōu)椤坝绖訖C(jī)”泛释。
如何讓線程變?yōu)椤坝绖訖C(jī)”
下面代碼可以做到
public class Thread{
public void run(){
Looper.prepare();
Looper.loop();
}
}
如上代碼滤愕,需要依次調(diào)用Looper.prepare()和Looper.loop()方法就可以讓線程變?yōu)椤坝绖訖C(jī)”,是不是非常的簡單怜校,那我們就從源碼角度一趟究竟间影。
線程變?yōu)椤坝绖訖C(jī)”-源碼分析
前置知識
fd:Linux系統(tǒng)中把一切都看做是文件,當(dāng)進(jìn)程打開現(xiàn)有文件或創(chuàng)建新文件時茄茁,內(nèi)核向進(jìn)程返回一個文件描述符魂贬,文件描述符就是內(nèi)核為了高效管理已被打開的文件所創(chuàng)建的索引,用來指向被打開的文件裙顽,所有執(zhí)行 I/O 操作的系統(tǒng)調(diào)用都會通過文件描述符付燥,文件描述符不一定是文件,也可以是一塊匿名內(nèi)存愈犹。
epoll:io多路復(fù)用技術(shù)键科,就是在一個線程或進(jìn)程中監(jiān)聽多個文件描述符是否可以執(zhí)行io操作的能力,handler使用了這種技術(shù)漩怎,在下面會詳細(xì)介紹
eventfd:類似于管道的概念勋颖,可以實(shí)現(xiàn)線程間的事件通知 eventfd原文
ThreadLocal:這個類的主要作用是保存當(dāng)前線程獨(dú)有的數(shù)據(jù)。
相關(guān)類介紹
下面的文件屬于android s
frameworks/base/core/java/android/os/Looper.java
frameworks/base/core/java/android/os/MessageQueue.java
libcore/ojluni/annotations/hiddenapi/java/lang/ThreadLocal.java
frameworks/base/core/jni/android_os_MessageQueue.cpp
system/core/libutils/Looper.cpp
那我們就分別從:Looper.prepare()和Looper.loop()這兩個方法作為分析起點(diǎn)來進(jìn)行分析
1. Looper.prepare()
public static void prepare() {
//調(diào)用prepare方法勋锤,quitAllowed值為true饭玲,代表允許結(jié)束loop
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//當(dāng)前線程已經(jīng)存在Looper,則不能再次創(chuàng)建叁执,拋異常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//sThreadLocal是一個ThreadLocal類型的靜態(tài)變量茄厘,它存儲Looper實(shí)例
[1.1]
sThreadLocal.set(new Looper(quitAllowed));
}
sThreadLocal.set(new Looper(quitAllowed)) 這行代碼的作用是把Looper實(shí)例存儲在ThreadLocal中矮冬,存儲下來的一個最大的目的就是:能更方便的獲取當(dāng)前Thread對應(yīng)的Looper。比如可以在代碼的任何位置蚕断,只要調(diào)用Looper.myLooper()方法就能非常方便的獲取到Looper對象欢伏。
1.1 Looper#Looper()
//Looper的構(gòu)造方法是私有的,只能調(diào)用Looper的靜態(tài)方法來創(chuàng)建Looper
private Looper(boolean quitAllowed) {
//new MessageQueue
[1.2]
mQueue = new MessageQueue(quitAllowed);
//保存當(dāng)前線程引用
mThread = Thread.currentThread();
}
1.2 MessageQueue#MessageQueue()
MessageQueue(boolean quitAllowed) {
//quitAllowed的值當(dāng)前為true
mQuitAllowed = quitAllowed;
//這一看就是進(jìn)入nativeInit jni方法亿乳,該方法返回一個指針硝拧,mPtr保存下來
[1.3]
mPtr = nativeInit();
}
1.3 android_os_MessageQueue.cpp#android_os_MessageQueue_nativeInit
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
//new一個NativeMessageQueue
[1.4]
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
//增加它的引用計數(shù)器
nativeMessageQueue->incStrong(env);
//把nativeMessageQueue指針轉(zhuǎn)化為jlong類型躯护,返回給java層齐疙,
return reinterpret_cast<jlong>(nativeMessageQueue);
}
1.4 android_os_MessageQueue.cpp#NativeMessageQueue
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
//先獲取當(dāng)前線程的Looper,不存在則創(chuàng)建匀哄,native層也存在一個Looper
mLooper = Looper::getForThread();
if (mLooper == NULL) {
//new 一個Looper出來
[1.5]
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
1.5 Looper.cpp#Looper
Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
//eventfd方法返回fd聊训,EFD_NONBLOCK的作用在調(diào)用read/write函數(shù)的時候 不阻塞抱究,EFD_CLOEXEC作用是在fork子進(jìn)程時候調(diào)用exec()方法的時,把fd close調(diào)带斑,這樣在子進(jìn)程就不存在相應(yīng)的fd了
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));
AutoMutex _l(mLock);
rebuildEpollLocked();
}
該方法中有幾個關(guān)鍵點(diǎn)需要介紹下:
eventfd:可以實(shí)現(xiàn)線程之間或者進(jìn)程之間通信鼓寺,eventfd類似于pipe,但是比 pipe 更高效勋磕,一方面它比 pipe 少用一個fd妈候,節(jié)省了資源;另一方面挂滓,eventfd 的緩沖區(qū)管理也簡單得多苦银,全部“buffer”一共只有8字節(jié)。eventfd在通信時候不能傳大數(shù)據(jù)赶站。
eventfd是如何實(shí)現(xiàn)線程之間的通信幔虏,通過下面的偽代碼來說明
//用來保持生成的fd
savedEventfd
//調(diào)用eventfd方法,返回的fd是阻塞類型的
savedEventfd = eventfd(0, EFD_CLOEXEC);
//下面方法發(fā)生于線程A中贝椿,調(diào)用read方法從savedEventfd中讀取int數(shù)據(jù)想括,因?yàn)閟avedEventfd是阻塞類型的,因此線程A會阻塞于read方法烙博,直到有數(shù)據(jù)為止
uint64_t counter;
read(savedEventfd,&counter,sizeof(uint64_t));
//下面方法調(diào)用發(fā)生于線程B中瑟蜈,調(diào)用write方法往savedEventfd中寫一個int值,因?yàn)閟avedEventfd寫入了數(shù)據(jù)习勤,上面線程A就會被喚醒,讀出剛剛寫入的int數(shù)據(jù)
uint64_t inc = 1;
write(savedEventfd,&inc,sizeof(uint64_t));
mWakeEventFd:它保存了eventfd方法返回的fd焙格,并且需要注意图毕,在調(diào)用eventfd方法的時候,傳遞了EFD_NONBLOCK這個參數(shù)眷唉,表示返回的fd是非阻塞類型予颤,即調(diào)用read囤官,write方法不會發(fā)生阻塞,上面的偽代碼是阻塞的蛤虐,現(xiàn)在卻是非阻塞類型的党饮,那又怎么實(shí)現(xiàn)線程之間或者進(jìn)程之間通信呢?答案是結(jié)合epoll機(jī)制驳庭。
mWakeEventFd的主要作用是java層的MessageQueue的喚醒/等待操作刑顺,喚醒/等待操作都是通過給mWakeEventFd寫數(shù)據(jù)和讀數(shù)據(jù)實(shí)現(xiàn)的。
1.6 Looper.cpp#rebuildEpollLocked
void Looper::rebuildEpollLocked() {
// Close old epoll instance if we have one.
// mEpollFd存在饲常,則重新設(shè)置
if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
mEpollFd.reset();
}
// Allocate the new epoll instance and register the wake pipe.
//調(diào)用epoll_create1方法重新創(chuàng)建蹲堂,這個方法會返回一個fd并賦值給mEpollFd
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd.get();
//通過epoll_ctl方法來添加一個event
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
strerror(errno));
//若mRequests中存在,則依次調(diào)用epoll_ctl方法添加event
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, strerror(errno));
}
}
}
在介紹上面方法之前贝淤,先來介紹下fd管道柒竞,管道,eventfd播聪,再來介紹epoll機(jī)制朽基。
管道,F(xiàn)IFO离陶,fd管道稼虎,eventfd
這節(jié)不是介紹handler機(jī)制嗎?怎么涉及到了管道這些內(nèi)容枕磁,主要原因是咱們的handler機(jī)制已經(jīng)不是單單解決線程之間通信的問題渡蜻,還解決進(jìn)程之間的通信。
管道:是半雙工的(就是數(shù)據(jù)只能在一個方向上流動)计济,只能有公共祖先的兩個進(jìn)程之間使用
FIFO:也是一種管道茸苇,它沒有管道的只能在公共祖先的兩個進(jìn)程之間使用的限制
fd管道:又稱UNIX 域套接字,它是全雙工的(一端既可以是寫端也可以是讀端)沦寂,并且對進(jìn)程沒有限制学密。vsync機(jī)制就使用的是fd管道
eventfd:上面已經(jīng)介紹了,它的優(yōu)點(diǎn)高效/少占用資源传藏,缺點(diǎn):傳播的數(shù)據(jù)只能是int腻暮。
這四者都可以實(shí)現(xiàn)線程之間/進(jìn)程之間的通信,并且它們的read毯侦,write的目標(biāo)都是fd(文件描述符)哭靖。它們實(shí)現(xiàn)通信的方式如上面eventfd的偽代碼一樣,在創(chuàng)建的時候需要設(shè)置為阻塞模式侈离,在阻塞模式下read试幽,write函數(shù)都是阻塞的,因此尤其read函數(shù)的調(diào)用就需要放在單獨(dú)的線程中卦碾。那如果創(chuàng)建了很多個管道來實(shí)現(xiàn)線程之間通信铺坞,那豈不是要創(chuàng)建很多的讀線程起宽,隨著管道的數(shù)量多起來,讀線程也增加济榨,這種方式肯定不是一個好的解決方案坯沪,解決這個問題的一個技術(shù)是IO多路復(fù)用。
epoll
IO多路復(fù)用:我的理解是比如原先創(chuàng)建一個阻塞型的管道擒滑,那就需要創(chuàng)建一個讀線程專門的來監(jiān)聽管道的讀端是否有數(shù)據(jù)腐晾,那創(chuàng)建n多個阻塞型管道,那就需要創(chuàng)建n多個讀線程橘忱;那IO多路復(fù)用就是只創(chuàng)建一個讀線程赴魁,來監(jiān)聽n多個管道讀端的數(shù)據(jù)。
IO多路復(fù)用的優(yōu)勢是不是很明顯钝诚,它有多種實(shí)現(xiàn):select機(jī)制颖御,poll機(jī)制,epoll機(jī)制凝颇。epoll機(jī)制是最高效潘拱,優(yōu)點(diǎn)最多的一個機(jī)制,用一段偽代碼來介紹下它的使用:
//1. 調(diào)用epoll_create1方法先創(chuàng)建拧略,該方法返回一個fd
epollFd = epoll_create1(EPOLL_CLOEXEC);
//2. 調(diào)用epoll_ctl方法添加一個event芦岂,epoll_ctl方法的第一個參數(shù)就是epollFd。這樣event就和epollFd綁定在了一起垫蛆,epollFd就可以監(jiān)聽event上的數(shù)據(jù)了
struct epoll_event eventItem;
//event類型
eventItem.events = EPOLLIN;
//event對應(yīng)的fd
eventItem.data.fd = pipeFd;
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
//3. 調(diào)用epoll_wait方法開始監(jiān)聽所有event的數(shù)據(jù)禽最。在沒有監(jiān)聽到數(shù)據(jù)的情況下,會進(jìn)入阻塞狀態(tài)袱饭,會釋放cpu等資源川无。參數(shù)timeoutMillis代表等待時間,== 0代表不等待立馬返回虑乖,== -1 則代表等待懦趋,直到等待數(shù)據(jù)為止,> 0則代表需要等待的時間疹味。
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
總結(jié)下epoll的用法:
- 初始化仅叫,調(diào)用epoll_create1方法創(chuàng)建并返回一個fd
- 添加event事件,調(diào)用epoll_ctl方法
- 等待數(shù)據(jù)到來糙捺,調(diào)用epoll_wait诫咱,它的參數(shù)timeoutMillis代表等待時間,== 0代表不等待立馬返回洪灯,== -1 則代表等待坎缭,直到等待數(shù)據(jù)為止,> 0則代表需要等待的時間。在沒有數(shù)據(jù)到來的時候幻锁,會進(jìn)入阻塞狀態(tài),會釋放cpu等資源
rebuildEpollLocked
那就來看下rebuildEpollLocked方法所做的事情:
- 調(diào)用epoll_create1方法創(chuàng)建并返回fd賦值給mEpollFd
- 把mWakeEventFd關(guān)聯(lián)的epoll_event边臼,調(diào)用epoll_ctl方法添加該事件哄尔,這樣mEpollFd就和mWakeEventFd產(chǎn)生了關(guān)聯(lián),就可以監(jiān)聽它的數(shù)據(jù)了
- 把mRequests中的請求也通過epoll_ctl方法添加這些事件柠并。mRequests包含了各種需要監(jiān)聽的fd岭接,比如:vsync機(jī)制就是通過fd管道實(shí)現(xiàn)的,其中一個fd在surfaceflinger進(jìn)程臼予,另外一個對應(yīng)的fd位于app進(jìn)程鸣戴,位于app進(jìn)程的fd會通過Looper.cpp#addFd方法把自己加入mRequests,這樣就可以通過epoll來監(jiān)聽是否有數(shù)據(jù)到來
1.7 小結(jié)
到此Looper.prepare()方法的流程就分析完了粘拾,用一張時序圖來看下整個調(diào)用流程
2.Looper.loop()
再來分析下Looper.loop()這個方法的流程
public static void loop() {
//獲取線程綁定的Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
省略代碼......
//啟動一個死循環(huán)
for (;;) {
[2.1]
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
public static @Nullable Looper myLooper() {
//從ThreadLocal中獲取Looper
return sThreadLocal.get();
}
2.1 Looper.loopOnce
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
//從MessageQueue中取Message窄锅,若沒有可執(zhí)行的Message 則block
[2.2]
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
省略代碼......
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
省略代碼......
msg.recycleUnchecked();
return true;
}
me.mQueue.next()會進(jìn)入MessageQueue.next()方法獲取Message,進(jìn)入該方法
2.2 MessageQueue.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;
//同樣起一個死循環(huán)
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//進(jìn)入jni方法, 這時候nextPollTimeoutMillis的值是-1
[2.3]
nativePollOnce(ptr, nextPollTimeoutMillis);
省略獲取Message的代碼...... (下一節(jié)會重點(diǎn)介紹)
}
}
nativePollOnce方法最終會調(diào)jni的android_os_MessageQueue_nativePollOnce方法
2.3 android_os_MessageQueue.cpp#android_os_MessageQueue_nativePollOnce
//obj java層的MessageQueue缰雇, ptr NativeMessageQueue指針入偷,timeoutMillis值為-1
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
//把ptr轉(zhuǎn)換為NativeMessageQueue指針
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
[2.4]
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
2.4 android_os_MessageQueue.cpp#nativeMessageQueue#pollOnce
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
//調(diào)用pollonce方法,timeoutMillis值為-1
[2.5]
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
2.5 Looper.cpp#pollOnce
//下面方法在system/core/libutils/include/utils/Looper.h
int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
inline int pollOnce(int timeoutMillis) {
//調(diào)用了pollOnce的重載方法械哟,outFd,outEvents,outData都為nullptr
return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
}
//下面方法在system/core/libutils/Looper.cpp
//timeoutMillis為-1
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
//同樣死循環(huán)
for (;;) {
//若有沒處理的response疏之,則處理,并返回
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != nullptr) *outFd = fd;
if (outEvents != nullptr) *outEvents = events;
if (outData != nullptr) *outData = data;
return ident;
}
}
//若result不為0,則返回暇咆,剛開始進(jìn)入這方法锋爪,result是0
if (result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != nullptr) *outFd = 0;
if (outEvents != nullptr) *outEvents = 0;
if (outData != nullptr) *outData = nullptr;
return result;
}
//進(jìn)入pollInner方法
[2.6]
result = pollInner(timeoutMillis);
}
}
2.6 Looper.cpp#pollInner
//timeoutMillis值為-1
int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif
// Adjust the timeout based on when the next message is due.
//因?yàn)閠imeoutMillis當(dāng)前值為-1 并且 mNextMessageUptime在Looper構(gòu)造方法初始化的時候,它的值為LLONG_MAX爸业,因此不會進(jìn)入下面調(diào)整timeout的邏輯
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d",
this, mNextMessageUptime - now, timeoutMillis);
#endif
}
// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mPolling = true;
//定義eventItems數(shù)組它主要接受傳遞過來的event其骄, 調(diào)用epoll_wait方法開始等待event,因?yàn)閠imeoutMillis當(dāng)前的值為-1,因此會阻塞等待events沃呢,并且釋放cpu等資源
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
省略掉處理wakefd和mRequest的代碼......(后面章節(jié)會詳細(xì)講解)
return result;
}
該方法會調(diào)用epoll_wait方法等待events年栓,因?yàn)閠imeoutMillis當(dāng)前的值為-1,因此會使當(dāng)前線程進(jìn)入阻塞狀態(tài),并釋放cpu等資源
2.7 小結(jié)
到此 Looper.loop()方法的流程就分析完了薄霜,用一張時序圖總結(jié)下:
總結(jié)
讓線程變?yōu)椤坝绖訖C(jī)”可以分為兩個步驟:
-
準(zhǔn)備階段某抓,Looper.prepare()方法其實(shí)做的都是準(zhǔn)備工作:
- 初始化Looper對象,并且把他放入ThreadLocal中惰瓜,放入ThreadLocal的主要作用就是:為了能在當(dāng)前線程的任何代碼處非常方便的獲取到當(dāng)前線程”綁定“的Looper否副。
- 初始化MessageQueue對象,它與上面初始化的Looper對象是一對一關(guān)系
- 初始化native層的Looper對象崎坊,調(diào)用eventfd方法創(chuàng)建fd备禀;調(diào)用epoll_create方法創(chuàng)建epollfd,并且依次調(diào)用epoll_ctl把mWakeEventfd以及mRequest封裝成event并添加,這樣就可以通過epoll機(jī)制來監(jiān)聽它所添加的event上面的事件是否發(fā)生了
-
開始工作階段曲尸,在線程中調(diào)用Looper.loop()方法后就開始工作了:
- Looper的loop方法會啟動一個死循環(huán)赋续,這樣一個線程就真正的變成“永動機(jī)”了
- 啟動死循環(huán)后,會調(diào)用MessageQueue的next方法從中獲取Message另患,next方法調(diào)用了nativePollOnce方法纽乱,最終會調(diào)用到Looper.cpp的pollInner方法,由于第一步準(zhǔn)備階段昆箕,epoll鸦列,eventfd相關(guān)的準(zhǔn)備工作都已經(jīng)準(zhǔn)備好了,pollInner方法中會調(diào)用epoll_wait方法等待事件到來鹏倘,因?yàn)檫@時候的等待時間(timeoutMillis)為-1,會一直等待薯嗤,直到有事件發(fā)生為止,進(jìn)而導(dǎo)致當(dāng)前的線程進(jìn)入阻塞狀態(tài)纤泵,并釋放cpu等資源
因?yàn)閔andler使用了epoll機(jī)制骆姐,handler既可以實(shí)現(xiàn)線程之間通信,也可以實(shí)現(xiàn)進(jìn)程之間通信捏题。
好了诲锹,到此線程已經(jīng)做好了一切準(zhǔn)備,就等待著“各種事件“的到來了涉馅。
思考
MessageQueue的next方法獲取消息時候 等待/喚醒 實(shí)現(xiàn)方案為啥沒用 wait/notify 來實(shí)現(xiàn)归园?
上面源碼分析提到MessageQueue的next方法最終是因?yàn)閑poll_wait方法,導(dǎo)致線程進(jìn)入 等待阻塞狀態(tài)的稚矿,那為啥沒有使用wait/notify來實(shí)現(xiàn)呢庸诱?大家其實(shí)可以找很早以前的android代碼,那時候確實(shí)是用wait/notify來實(shí)現(xiàn) 等待/喚醒機(jī)制晤揣。
我認(rèn)為的主要原因是:用epoll機(jī)制實(shí)現(xiàn)的 等待/喚醒機(jī)制桥爽,主要是它有如下優(yōu)點(diǎn):
- 功能強(qiáng)大:不僅實(shí)現(xiàn)線程之間通信,還實(shí)現(xiàn)進(jìn)程之間通信的功能昧识,vsync機(jī)制就是利用fd管道實(shí)現(xiàn)進(jìn)程通信钠四,epoll只需要監(jiān)聽fd管道的一端fd上的數(shù)據(jù)狀態(tài)即可,surfaceflinger進(jìn)程往對端fd上寫數(shù)據(jù)跪楞,epoll在當(dāng)前的線程中就可以監(jiān)聽到surfaceflinger發(fā)過來的數(shù)據(jù)缀去。
- 高性能: epoll機(jī)制可以監(jiān)聽n多個fd,并且不會隨著fd的增加而性能下降
- 擴(kuò)展性好: 只需要調(diào)用Looper.cpp的addFd方法就可以在當(dāng)前線程監(jiān)聽fd上的數(shù)據(jù)
- 既可以為java層提供服務(wù)甸祭,也可以為native層提供服務(wù):native層的Looper.cpp類其實(shí)也提供了和上層MessageQueue相關(guān)的功能
假如使用wait/notify實(shí)現(xiàn)進(jìn)程之間通信就困難了缕碎,并且即使實(shí)現(xiàn)了還可能會涉及到線程之間的切換,性能方面肯定大打折扣池户。
為啥要用eventfd機(jī)制咏雌?
在把Message放入MessageQueue的時候凡怎,這時候只是需要給阻塞的MessageQueue發(fā)一個有多簡單就能多簡單的通知或者信號就行,告訴它有消息到達(dá)因赊抖。為沒有用 wait/notify 來實(shí)現(xiàn) 等待/喚醒统倒,所以就需要用管道這類技術(shù)來實(shí)現(xiàn),但是用管道做這種事情又大才小用了氛雪,evenfd是最合適檐薯,它占用的內(nèi)存非常小并且只使用一個fd,并且它就是發(fā)送一個int類型的值就可以注暗,因此使用了eventfd來實(shí)現(xiàn):把Message放入MessageQueue的時候,通知阻塞的MessageQueue有消息到來了這樣的功能墓猎。