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.
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/256403.html
標籤:其他
上一篇:微信小程式之藍牙列印
下一篇:學習Makefile的記錄
