Handler原始碼學習記錄(java層、native層)
阿新 • • 發佈:2021-02-04
Handler原始碼學習記錄(java層、native層)
宗旨:學習記錄我看得懂就行!!!
模仿Handler原理,使用eventfd+epoll實現Handler基礎功能的小案例 -> gayhub地址(MessageQueueDemo)
java層
Handler.java(執行緒間切換的工具類)
三種訊息型別
同步訊息:最常用的訊息;
屏障訊息(同步屏障):該訊息無target。在訊息佇列中插入後會擋住後邊的所有同步訊息讓非同步訊息先走。撤銷該屏障同步訊息才能繼續通行;
非同步訊息:享有優先權的訊息。
//構函式中有boolean async傳參的,都是隱藏的不希望開發者使用。
//mAsynchronous 作用是讓該Handler傳送的訊息全部都是非同步訊息。
//開發者如果需要用到非同步訊息,將Message手動setAsynchronous就可以了。
@hide
public Handler(boolean async) {
...
mAsynchronous = async;
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Looper.java
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //建立佇列
mThread = Thread.currentThread(); //用來判斷是否在當前執行緒
}
//靜態方法
private static void prepare (boolean quitAllowed) {
// 引數quitAllowed,是否允許Looper退出。
// MainLooper prepareMainLooper 中是false,主執行緒的Looper不允許退出。子執行緒的Looper是允許退出的。
...
sThreadLocal.set(new Looper(quitAllowed)); //Looper保證執行緒唯一
...
}
//靜態方法
public static void loop() {
//由於靜態方法,無法直接使用mQueue
//從sThreadLocal中取Looper,確保取到的是對應的執行緒Looper
final Looper me = myLooper();
if (me == null) { //prepare檢查
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...
for (;;) { //死迴圈,這就是主執行緒不退出的原因。
//從佇列獲取Message
Message msg = queue.next(); //會阻塞
...
try {
msg.target.dispatchMessage(msg); //執行事件
...
}
...
msg.recycleUnchecked(); //釋放該Message
}
...
}
//注意,這不是靜態方法,能直接使用mQueue
public void quit() { //不再接受訊息,並且清空所有訊息(包括:延遲訊息、非延遲訊息),最後退出。
mQueue.quit(false);
}
//注意,這不是靜態方法,能直接使用mQueue
public void quitSafely() { //安全退出,不再接受訊息,並且清空所有延遲訊息。會將所有非延遲訊息都派發出去,才退出。
mQueue.quit(true);
}
MessageQueue.java
備註:註釋中 Msg代表同步訊息,Msg(A)代表非同步訊息,|代表屏障訊息
//native方法
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr); //是否正在輪詢
private final boolean mQuitAllowed; //是否允許退出(主執行緒是false的)
private long mPtr; //NativeMessageQueue指標地址,靠它強轉回NativeMessageQueue*物件
Message mMessages; //訊息佇列Head(連結串列)
private boolean mQuitting; //是否退出中
private boolean mBlocked; //是否在阻塞中
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
//訊息入隊方法
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.when = when; //執行時間 (系統時間 + 延遲時間)
Message p = mMessages;
boolean needWake; //是否需要喚醒
//情況1:當前佇列無訊息
//情況2:使用sendMessageAtFrontOfQueue方法入隊,這個放啊when就是為0
//情況3:新來的這條訊息執行時間比佇列中所有訊息的執行時間都要快,給它先執行。
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}else {
//當前正在阻塞中
//p.target == null 佇列頭是屏障訊息
//新來的這條訊息是非同步訊息
//需要出發喚醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
//情況1
//入隊的是Msg2
//當前佇列 Msg1 -> null
//上邊p的賦值,p = Msg1
//for (;;) {
// prev = p; //prev = Msg1
// p = p.next; //p = null
// if (p == null) { //退出迴圈
// break;
// }
//}
//msg.next = p; //Msg2 -> null
//prev.next = msg; //Msg1 -> Msg2 -> null
//情況2 (如果Msg3 when 小於 Msg2,那麼會走if的情況3)
//入隊的是Msg3(when 15)
//當前佇列 Msg2(when 10) -> Msg1(when 20) -> null
//上邊p的賦值,p = Msg2
//for (;;) {
// prev = p; //prev = Msg2
// p = p.next; //p = Msg1
// if (when < p.when) { //15 < 20 退出迴圈
// break;
// }
//}
//msg.next = p; //Msg3 -> Msg1 -> null
//prev.next = msg; //Msg2 -> Msg3 -> Msg1 -> null
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//情況3
//入隊的是Msg3
//當前佇列 | -> Msg2(A) -> Msg1 -> null
//prv = |
//p = Msg2(A)
//msg.next = p; // Msg3 -> Msg2(A) -> Msg1 -> null
//prev.next = msg; // | -> Msg3 -> Msg2(A) -> Msg1 -> null
if (needWake && p.isAsynchronous()) {
//有屏障訊息會先執行Msg2(A),但是呢Msg2(A)時辰未到,不能喚醒。
needWake = false;
}
}
//新夥伴入隊後連線連結串列
msg.next = p;
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
//訊息出隊方法
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0; //下一次迴圈的休眠時長
for (;;) {
...
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) { //當前msg是屏障訊息
//尋找非同步訊息,一個個找,直到找到非同步訊息就退出迴圈
//這時prevMsg肯定是一個同步訊息,msg肯定是非同步訊息
//例子 | -> Msg3 -> Msg2(A) -> Msg1 -> null
do {
prevMsg = msg; //prevMsg = Msg3
msg = msg.next; //msg = Msg2(A)
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 這條訊息執行時間還沒到,設定一個休眠時長,準備進入休眠。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
if (prevMsg != null) {
//prevMsg不為空,證明msg是非同步訊息。那麼把佇列給連線上
//例子 佇列變成 | -> Msg3 -> Msg1 -> null
prevMsg.next = msg.next;
} else {
//msg為同步訊息
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
//沒有訊息
nextPollTimeoutMillis = -1;
}
if (mQuitting) { //Looper.quit()呼叫,觸發退出邏輯。
dispose();
return null;
}
//既然都要MessageQueue都準備要阻塞了,那我們來乾點別的吧!!!
//MessageQueue提供了IdleHandler佇列,讓我們在當前執行緒空閒的時候,做一些不那麼耗時的事情。
//這樣就可以做優先順序低的業務邏輯從而提高效能。(例如:在主執行緒中,防止訊息過多導致ui卡頓,可以適當將優先順序低的邏輯放到IdleHandler去處理)
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size(); //獲取IdleHandler佇列數量
}
if (pendingIdleHandlerCount <= 0) { //連IdleHandler佇列都沒東西處理,那就阻塞吧
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//遍歷IdleHandler佇列,呼叫其queueIdle方法,處理開發者的邏輯。
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
//keep是提供給開發者選擇的,該IdleHandler是一次性的還是重複利用的。
//true:執行完後不從IdleHandler佇列中移除,下一次空閒繼續執行。
//false:執行完後就從IdleHandler佇列中移除了
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;
}
}
//屏障訊息入隊方法
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++; //屏障訊息的身份id,用於移除令牌的
final Message msg = Message.obtain();
msg.markInUse(); //預設就是使用中了
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
//|(when )
//例子:屏障訊息:|(when 5),當前佇列:Msg2(when 2)-> Msg1(when 5)-> null
//迴圈後:prev = Msg1,p = null
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) {
msg.next = p; // | -> null
prev.next = msg;// Msg1 -> | -> null
//佇列變成:Msg2 -> Msg1 -> | -> null
} else {
msg.next = p;
mMessages = msg;
}
return token; //返回屏障訊息的身份id
}
}
//移除屏障訊息方法
//注意:從next()邏輯可以看到,屏障訊息是不會出隊的,只能使用removeSyncBarrier方法才能移除掉。
public void removeSyncBarrier(int token) { //傳入訊息屏障身份id
synchronized (this) {
Message prev = null;
Message p = mMessages;
//找到token對應的屏障訊息
//走完迴圈時,p就是該屏障訊息
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;
//prev不為空,證明這個屏障訊息之前還有沒有處理的訊息
//什麼情況下prev不為空?可能是這個屏障訊息不是第一個屏障吧,第2個?第3個?...
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);
}
}
}
//退出訊息佇列
void quit(boolean safe) { //是否為安全退出
...
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
//不安全退出方法
//一個個訊息釋放掉
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
//安全退出方法
//將延時訊息都釋放掉,保留非延時訊息,讓這些訊息執行完。
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
//例如:
//now = 5; 佇列:Msg3(when 2)-> Msg2(when 4)-> Msg1(when 6)-> null
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
//經過迴圈後,n = Msg1,p = Msg2
p.next = null; //斷開Msg2後邊的隊伍
//釋放後邊的延時訊息
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
native層(eventfd + epoll)
我看的是android6.0原始碼,eventfd負責通知,不知道啥版本之前是用pipe(管道)實現的通知後面被eventfd取代。epoll(IO多路複用)負責監聽。這兩個系統呼叫的科普在下面註釋會有。
為啥?我理解是eventfd佔用的fd比pipe要少,pipe要佔用兩個一個讀一個寫。
原始碼檔案整合(位置 -> MessageQueueDemo/native_source_code/)
原始碼地址:
android-6.0/system/core/libutils/Looper.cpp
android-6.0/system/core/include/utils/Looper.h
android-6.0/frameworks/base/core/jni/android_os_MessageQueue.cpp
android-6.0/frameworks/base/core/jni/android_os_MessageQueue.h
/*
備註:
native Looper中
函式
addFd
removeFd
sendMessage
sendMessageDelayed
removeMessages
...
結構體
Message
Request
Response
...
類
MessageHandler
WeakMessageHandler
...
還有向量mRequest、mResponse等等
都是提供給native開發者使用訊息佇列相關邏輯(可以理解為native層的handler)。
與java層無關的。(o(╥﹏╥)o痛苦,剛開始看一臉懵逼。)
*/
//java層中native方法對應的函式
static JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
{ "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
{ "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },
{ "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
{ "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling },
{ "nativeSetFileDescriptorEvents", "(JII)V",
(void*)android_os_MessageQueue_nativeSetFileDescriptorEvents },
};
//註冊JNI方法
int register_android_os_MessageQueue(JNIEnv* env) {
int res = RegisterMethodsOrDie(env, "android/os/MessageQueue", gMessageQueueMethods,
NELEM(gMessageQueueMethods));
jclass clazz = FindClassOrDie(env, "android/os/MessageQueue");
gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J");
gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz,
"dispatchEvents", "(II)I");
return res;
}
//❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀
//native層核心是Looper對eventfb + epoll的封裝。
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); //建立一個本地的訊息佇列
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
//RefBase相關文章 https://blog.csdn.net/u012124438/article/details/71075423
nativeMessageQueue->incStrong(env); //強引用指標計數,智慧指標(RefBase)
//強轉為jlong,這個jlong是nativeMessageQueue地址
//並儲存到java層,之後java層便可以通過這個地址,強轉回nativeMessageQueue指標
return reinterpret_cast<jlong>(nativeMessageQueue);
}
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread(); //從當前執行緒中獲取Looper
if (mLooper == NULL) { //為空則建立並儲存
mLooper = new Looper(false);
//通過 pthread_getpecific 和 pthread_setspecific 保證執行緒唯一
//類似於java ThreadLocal
Looper::setForThread(mLooper);
}
}
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
/*
eventfd相關知識
api:
建立一個eventfd物件(就像是開啟一個eventfd的檔案,類似普通檔案的open操作。)
int eventfd(unsigned int initval, int flags) 用來實現程序(執行緒)間的 等待/通知(wait/notify) 機制
initval:該物件是一個核心維護的無符號的64位整型計數器。初始化為initval的值。
flags:
EFD_CLOEXEC:檔案被設定成 O_CLOEXEC,簡單說就是fork子程序時不繼承,對於多執行緒的程式設上這個值不會有錯的。
EFD_NONBLOCK:功能同open的O_NONBLOCK,設物件為非阻塞狀態。
如果沒有設定這個狀態的話,read讀eventfd,並且計數器的值為0就一直堵塞在read呼叫當中。
要是設定了這個標誌,就會返回一個EAGAIN錯誤(errno = EAGAIN)。
EFD_SEMAPHORE:支援semophore語義的read,簡單說read一次值就減1
return:用於事件通知的檔案描述符
write():設定counter值。多次呼叫counter會累加,例: write(1);write(2); write(3); -> counter為6
read():讀取counter值,並將counter值置0,如果是semophore就減1。
*/
mWakeEventFd = eventfd(0, EFD_NONBLOCK); //mWakeEventFd這裡表示喚醒Looper的檔案描述符
LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd. errno=%d", errno);
AutoMutex _l(mLock);
rebuildEpollLocked();
}
void Looper::rebuildEpollLocked() {
if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
//如果存在舊的epoll控制代碼,就先關閉。
close(mEpollFd);
}
/*
epoll相關知識
(select/poll/epoll都是IO多路複用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程式進行讀或寫操作。本質上select/poll/epoll都是同步I/O,即讀寫是阻塞的。)
在 select/poll中,程序只有在呼叫一定的方法後,核心才對所有監視的檔案描述符進行掃描,
而epoll事先通過epoll_ctl()來註冊一個檔案描述符,一旦基於某個檔案描述符就緒時,
核心會採用類似callback的回撥機制,迅速啟用這個檔案描述符,當程序呼叫epoll_wait()
時便得到通知。(此處去掉了遍歷檔案描述符,而是通過監聽回撥的的機制。這正是epoll的魅力所在。)
epoll優勢
監視的描述符數量不受限制,所支援的FD上限是最大可以開啟檔案的數目,具體數目可以cat /proc/sys/fs/file-max檢視,一般來說這個數目和系統記憶體關係很大,以3G的手機來說這個值為20-30萬。
IO效能不會隨著監視fd的數量增長而下降。epoll不同於select和poll輪詢的方式,而是通過每個fd定義的回撥函式來實現的,只有就緒的fd才會執行回撥函式。
如果沒有大量的空閒或者死亡連線,epoll的效率並不會比select/poll高很多。但當遇到大量的空閒連線的場景下,epoll的效率大大高於select/poll。
api:
建立函式
int epoll_create(int size);
size:監聽的描述符個數。內部支援動態擴充套件的。
return:返回epoll的fd(ls /proc/<pid>/fd/ 可查;用完epoll後必須呼叫close()關閉否則可能導致fd被耗盡。)
事件註冊函式
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd:是epoll_create()的返回值;
op:表示op操作,用三個巨集來表示,分別代表新增、刪除和修改對fd的監聽事件;
EPOLL_CTL_ADD (新增)
EPOLL_CTL_DEL (刪除)
EPOLL_CTL_MOD(修改)
fd:需要監聽的檔案描述符;
epoll_event:需要監聽的事件,struct epoll_event結構如下:
struct epoll_event {
__uint32_t events; //Epoll事件
events可取值:(表示對應的檔案描述符的操作)
EPOLLIN :可讀(包括對端SOCKET正常關閉);
EPOLLOUT:可寫;
EPOLLERR:錯誤;
EPOLLHUP:中斷;
EPOLLPRI:高優先順序的可讀(這裡應該表示有帶外資料到來);
EPOLLET: 將EPOLL設為邊緣觸發模式,這是相對於水平觸發來說的。
EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之後就不再監聽該事件
epoll_data_t data; //使用者可用資料
};
return:0:註冊成功 <0:出現錯誤,需要檢查 errno錯誤碼判斷錯誤型別
等待事件
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
epfd:等待epfd上的io事件,最多返回maxevents個事件;
events:用來從核心得到事件的集合;
maxevents:events數量,該maxevents值不能大於建立epoll_create()時的size;
timeout:超時時間(毫秒,0會立即返回)。
return:0:超時返回 >0:有n個fd觸發事件 <0:出現錯誤,需要檢查 errno錯誤碼判斷錯誤型別
*/
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno);
struct epoll_event eventItem;
//memset函式:作用是在一段記憶體塊中填充某個給定的值,它對較大的結構體或陣列進行清零操作的一種最快方法
memset(& eventItem, 0, sizeof(epoll_event));
eventItem.events = EPOLLIN; //可讀事件
eventItem.data.fd = mWakeEventFd; //eventfd的fd排上用場了。
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem); //註冊epoll事件監聽
...
}
//❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
...
}
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) { //死迴圈
...
/*
enum {
POLL_WAKE = -1, //表示Looper的wake方法被呼叫,write事件觸發
POLL_CALLBACK = -2, //表示某個被監聽fd被觸發。
POLL_TIMEOUT = -3, //表示等待超時
POLL_ERROR = -4, //表示等待期間發生錯誤
};
*/
if (result != 0) { //當result不等於0時,就會跳出迴圈,返回到java層
...
return result;
}
result = pollInner(timeoutMillis);
}
}
int Looper::pollInner(int timeoutMillis) {
...
struct epoll_event eventItems[EPOLL_MAX_EVENTS]; //事件集合(eventItems),EPOLL_MAX_EVENTS為最大事件數量,它的值為16
//等待事件發生或者超時(timeoutMillis),如果有事件發生就會將放入事件集合(eventItems),返回的eventCount為事件數量
//如果沒有事件發生進入休眠等待,如果timeoutMillis時間後還沒有被喚醒,也會返回0
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
...
// Check for poll error.
if (eventCount < 0) {
...
result = POLL_ERROR;
...
}
// Check for poll timeout.
if (eventCount == 0) {
...
result = POLL_TIMEOUT;
...
}
// Handle all events.
...
for (int i = 0; i < eventCount; i++) { //遍歷事件集合(eventItems),處理事件
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) { //處理eventfd(java層的事件)
if (epollEvents & EPOLLIN) {
awoken();
} else { //其他檔案描述符,就進行它們自己的處理邏輯
...
}
} else {
...
}
}
//下面是處理Native的Message
...
return result;
}
void Looper::awoken() {
...
uint64_t counter;
//該TEMP_FAILURE_RETRY巨集定義 用於忽略系統中斷造成的錯誤。常用於系統呼叫。
//將eventfd的資料讀出來,其實就是一個消費的動作。
TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
//❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀
void NativeMessageQueue::wake() {
mLooper->wake();
}
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
//向eventfd寫入1,write呼叫這樣就會啟用epoll,從而讓pollOnce返回到java層
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);
}
}
}
//❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->decStrong(env); //智慧指標 強引用計數減1,當引用數為0會自動呼叫解構函式
}
總結:
read the fucking source code.