Android消息处理机制(Handler,Looper,MessageQueue和Message)
原创英文链接
http://www.programering.com/a/MjM2QDMwATc.html
翻译版:
Android是一种消息驱动,消息驱动的几个要素:
- 消息说:消息
- 消息队列:MessageQueue
- 新闻周期,删除流通消息处理:Looper
- 消息处理,来自消息队列的消息循环消息应该在处理消息之后执行:Handler
通常我们最常使用的是Message和Handler,如果你使用HandlerThread或类似的HandlerThread可能会接触到Looper,而在Looper内部使用MessageQueue,对于标准的SDK,我们无法实例化和使用(构造函数是包可见性) 。
我们通常会接触到Looper,Message,Handler都是通过JAVA实现的,基于Linux的Android系统基于Android系统,底层存在C,C ++和NDK,消息驱动模型怎么只存在于JAVA层,实际上, Native层中对应于Java层,如Looper,MessageQueue。
初始化消息队列
首先看一个线程是否应该实现消息循环,以HandlerThread为例:
public void run(){ mTid = Process.myTid(); Looper.prepare(); 同步(this ){ mLooper = Looper.myLooper(); notifyAll的(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop() mTid = -1 ; }
是两个红色标记,第一次调用准备初始化MessageQueue和Looper,然后调用循环进入消息循环。首先看看Looper.prepare。
public static void prepare(){ 准备(真); } private static void prepare(boolean quitAllowed){ if(sThreadLocal.get()!= null ){ throw new RuntimeException(“每个线程只能创建一个Looper” ); } sThreadLocal.set(new Looper(quitAllowed)); }
重载的函数,quitAllowed默认为true,从名称可以看出是否消息循环可以退出,默认是退出,主线程(UI线程)初始化消息循环称为prepareMainLooper,pass为false。使用ThreadLocal,每个线程都可以初始化一个Looper。
在初始化中看看Looper:
私人 Looper(布尔quitAllowed){ mQueue = new MessageQueue(quitAllowed); mRun = true ; mThread = Thread.currentThread(); } MessageQueue(boolean quitAllowed){ mQuitAllowed = quitAllowed; nativeInit(); }
在Looper初始化中,创建一个MessageQueue对象保存在mQueue成员中。MessageQueue构造函数是包的可见性,所以我们不能直接使用,而MessageQueue初始化称为nativeInit,它是一个Native方法:
static void android_os_MessageQueue_nativeInit(JNIEnv * env,jobject obj){ NativeMessageQueue * nativeMessageQueue = new NativeMessageQueue(); if(!nativeMessageQueue){ jniThrowRuntimeException(env,“ 无法分配本地队列” ); 返回; } nativeMessageQueue - > incStrong(env); android_os_MessageQueue_setNativeMessageQueue(env,obj,nativeMessageQueue); } static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv * env,jobject messageQueueObj, NativeMessageQueue * nativeMessageQueue){ env - > SetIntField(messageQueueObj,gMessageQueueClassInfo.mPtr, reinterpret_cast <jint> (nativeMessageQueue)); }
在nativeInit中,Native层MessageQueue的新对象和存储在mPtr的MessageQueue成员的Java层中的地址中,有很多Android的实现,一个类已经在Java层和Native层中实现, GetFieldID和SetIntField JNI为本地层类保存了Java层的地址,以用于类mPtr成员(如Parcel)的实例。
然后NativeMessageQueue的实现:
NativeMessageQueue :: NativeMessageQueue():mInCallback(false ),mExceptionObj(NULL){ mLooper = Looper :: getForThread(); if(mLooper == NULL){ mLooper = 新 Looper(false ); 活套:: setForThread(mLooper); } }
获取NativeMessageQueue构造函数中的Looper对象的Native层,Native层Looper也使用线程本地存储,请注意,新Looper引入了参数false。
Looper :: Looper(bool allowNonCallbacks): mAllowNonCallbacks(allowNonCallbacks),mSendingMessage(false ), mResponseIndex(0 ),mNextMessageUptime(LLONG_MAX){ int wakeFds [ 2 ]; int result = pipe(wakeFds); LOG_ALWAYS_FATAL_IF(result!= 0,“ 无法创建唤醒管道。errno =%d ” ,errno); mWakeReadPipeFd = wakeFds [0]; mWakeWritePipeFd = wakeFds [1 ]; result = fcntl(mWakeReadPipeFd,F_SETFL,O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result!= 0,“ 无法使唤醒读取管道非阻塞。errno =%d ” , 错误号); result = fcntl(mWakeWritePipeFd,F_SETFL,O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result!= 0,“ 无法使唤醒写入管道非阻塞。errno =%d ” , 错误号); // 分配epoll实例并注册唤醒管道。 mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd < 0,“ 无法创建epoll实例。errno =%d ” ,errno); struct epoll_event eventItem; memset(&eventItem,0,sizeof(epoll_event)); // 将未使用的数据成员字段union 归零eventItem.events = EPOLLIN; eventItem.data.fd = mWakeReadPipeFd; result = epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem); LOG_ALWAYS_FATAL_IF(result!= 0,“ 无法将唤醒读取管道添加到epoll实例。errno =%d ” , 错误号); }
使用epoll的Looper中的本地图层。初始化一个管道,保留mWakeWritePipeFd和mWakeReadPipeFd以写入和读取管道末端,并监视EPOLLIN事件的读取结束。请注意初始化程序的值列表,mAllowNonCallbacks的值为false。
什么是mAllowNonCallback?使用epoll监视mWakeReadPipeFd事件?实际上,Native Looper不仅可以监视描述符,Looper还提供addFd方法:
int addFd(int fd,int ident,int events,ALooper_callbackFunc callback,void * data); int addFd(int fd,int ident,int events,const sp <LooperCallback>&callback,void * data);
FD说监视描述符。Ident表示监视事件标识,Value必须>或ALOOPER_POLL_BACK; = 0(-2),Event表示监视事件,Callback是事件发生时的回调函数,这是mAllowNonCallbacks的作用,当mAllowNonCallbacks为true时,允许回调为NULL,Ident在pollOnce中作为返回结果,否则,不允许回调为空,当回调不为NULL时,ident的值将被忽略。或者只是看看易于理解的代码:
int Looper :: addFd(int fd,int ident,int events,const sp <LooperCallback>&callback,void * data){ #if DEBUG_CALLBACKS ALOGD(“ %p〜addFd - fd =%d,ident =%d,events = 0x%x,callback =%p,data =%p “,this ,fd,ident, 事件,回调。get (),data); #ENDIF 如果(!回调。获得()){ 如果(!mAllowNonCallbacks){ ALOGE(“ 无效尝试设置NULL回调但不允许此循环。” ); 回报 - 1 ; } if(ident < 0 ){ ALOGE(“ 无效尝试设置NULL回调与身份<0。” ); 回报 - 1 ; } } else { ident = ALOOPER_POLL_CALLBACK; } int epollEvents = 0 ; if(events&ALOOPER_EVENT_INPUT)epollEvents | = EPOLLIN; if(events&ALOOPER_EVENT_OUTPUT)epollEvents | = EPOLLOUT; { // 获取锁定 AutoMutex _l(mLock); 请求请求; request.fd = fd; request.ident = ident; request.callback = callback; request.data = data; struct epoll_event eventItem; memset(&eventItem,0,sizeof(epoll_event)); // 清零未使用的数据成员字段union unionItem.events = epollEvents; eventItem.data.fd = fd; ssize_t requestIndex = mRequests.indexOfKey(fd); if(requestIndex < 0 ){ int epollResult = epoll_ctl(mEpollFd,EPOLL_CTL_ADD,fd,&eventItem); if(epollResult < 0 ){ ALOGE(“ 为fd%d添加epoll事件时出错,errno =%d ” ,fd,errno); 回报 - 1 ; } mRequests.add(fd,request); } else { int epollResult = epoll_ctl(mEpollFd,EPOLL_CTL_MOD,fd,&eventItem); if(epollResult < 0 ){ ALOGE(“ 错误修改fd%d的epoll事件,errno =%d ” ,fd,errno); 回报 - 1 ; } mRequests.replaceValueAt(requestIndex,request); } } // 释放锁定 返回 1 ; }
如果回调为空,将检查是否允许mAllowNonCallbacks回调为空,如果回调为空则将确定ident> = 0。如果回调不是空的ALOOPER_POLL_CALLBACK的ident值,无论是什么值。
然后将传入值的参数封装到一个Request结构中,并将描述符保存到一个KeyedVector的mRequests中,然后通过epoll_ctl添加或替换(如果描述符在调用addFD之前添加监听)对这个描述符的监听。
类图:
发送一个消息
通过Looper.prepare初始化后的消息队列可以在Looper.loop进入消息循环后调用,然后我们可以发送消息给消息队列,消息循环会将消息处理消除,在看到消息处理之前查看消息是如何被添加到消息队列中的。
在Java层,Message类表示一个消息对象,发送消息时必须先获得一个消息对象,Message类的构造函数是public的,但不建议直接将new Message,Message存储在消息池的缓存中,我们可以从缓冲池得到一条消息,使用获得的消息,使用系统调用回收消息后,如果他们新增了很多消息,每次使用系统进入缓冲池后,都会占用大量的内存,如图所示下面:
public static Message get (){ synchronized (sPoolSync){ if(sPool!= null ){ 消息m = sPool; sPool = m.next; m.next = null ; sPoolSize - ; 返回m; } } 返回 新的消息(); } public void recycle(){ clearForRecycle(); synchronized (sPoolSync){ if(sPoolSize < MAX_POOL_SIZE){ next = sPool; sPool = this ; sPoolSize ++ ; } } }
内部消息通过下一个成员实现列表,例如用于缓存消息列表的sPool。
消息对象获取如何发送它,我们都知道是由Handler post,sendMessage和其他方法实现的,但这些方法最终都是通过sendMessageAtTime方法调用的:
public boolean sendMessageAtTime(Message msg,long uptimeMillis){ MessageQueue队列 = mQueue; if(queue == null ){ RuntimeException e = new RuntimeException( this +“sendMessageAtTime()called no mQueue” ); Log.w( “Looper” ,e.getMessage(),e); 返回 false ; } 返回enqueueMessage(队列,味精,uptimeMillis); }
SendMessageAtTime访问消息队列,然后调用enqueueMessage方法,mQueue从与Handler Looper关联的消息队列中获取。
private boolean enqueueMessage(MessageQueue queue,Message msg,long uptimeMillis){ msg.target = this ; 如果(mAsynchronous){ msg.setAsynchronous(真); } 返回queue.enqueueMessage(msg,uptimeMillis); }
EnqueueMessage将消息目标设置为当前处理程序,然后调用MessageQueue enqueueMessage,在调用queue.enqueueMessage之前判断mAsynchronous,从名称是异步消息的手段,了解Asynchronous的作用,需要了解一个概念Barrier。
屏障和异步消息
Barrier是什么意思,从名字来说是一个拦截器,后面的拦截器是暂时无法执行的消息,直到这个拦截器被删除后,MessageQueue才有一个叫enqueueSyncBarier的函数可以添加一个Barrier。
int enqueueSyncBarrier(long when){ // 排队一个新的同步障碍标记。 // 我们不需要唤醒队列,因为障碍的目的是阻止它。 synchronized(this ){ final int token = mNextBarrierToken ++ ; 最终消息msg = Message.obtain(); msg.arg1 = 令牌; 消息prev = null ; 消息p = mMessages; if(when!= 0 ){ while(p!= null && p.when <= when){ prev = p; p = p.next; } } if(prev!= null){ // invariant:p == prev.next msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = 味精; } 返回令牌; } }
在enqueueSyncBarrier中,获取一条消息,并设置msg.arg1 = token,令牌只是每次调用enqueueSyncBarrier后自增加int值以来,Objective只返回一个令牌,每次调用enqueueSyncBarrier时,该消息还需要设置执行时间,然后插入到消息队列中,特别的是该消息未设置为目标,Msg.target为空。
输入消息循环将在实现中保留MessageQueue的消息,调用下一个函数MessageQueue,其中有:
消息msg = mMessages; 如果(msg!= null && msg.target == null ){ // 被障碍阻挡。在队列中查找下一个异步消息。 做{ prevMsg = msg; msg = msg.next; } while(msg!= null &&!msg.isAsynchronous()); }
如果消息目标null的队列头部表示它是Barrier,因为只有两种方法可以添加mMessages消息,一个是enqueueMessage,另一个是enqueueBarrier,如果mst.target null是直接抛出异常,则enqueueMessage,会看到后面。
异步消息调用实际上就是这种情况,我们可以通过enqueueBarrier向消息队列中插入一个Barrier,然后在Barrier时间执行同步消息队列,将Barrier拦截器执行到不能执行,直到我们调用removeBarrier,移除Barrier,并异步消息没有作用,默认是同步消息的消息,除非我们调用Message setAsynchronous,这个方法是隐藏的。只有在Handler初始化参数中,Handler初始化参数指定的消息所发送的消息是异步的,这样它才会在Handler的enqueueMessage中调用Message setAsynchronous,设置消息是异步的,从上面的Handler.enqueueMessage代码中可以看出。
所谓异步消息,只有一个,就是在设置Barrier时仍然可以不受障碍的影响是正常的,如果没有设置Barrier,异步消息没有区别于同步消息,可以通过removeSyncBarrier去除障碍:
void removeSyncBarrier(int token){ // 从队列中移除一个同步障碍标记。 // 如果队列不再被障碍阻塞,那么将其唤醒。 final boolean needWake; 同步(this ){ 消息prev = null ; 消息p = mMessages; while(p!= null &&(p.target!= null || p.arg1!= token)){ prev = p; p = p.next; } if(p == null ){ throw new IllegalStateException(“指定的消息队列同步” +“障碍标记尚未发布或已被删除。” ); } if(prev!= null ){ prev.next = p.next; needWake = false ; } else { mMessages = p.next; needWake = mMessages == null || mMessages.target!= null ; } p.recycle(); } if (needWake){ nativeWake(mPtr); } }
参数标记的返回值是enqueueSyncBarrier,如果不是不存在的异常将被抛出,则调用指定的标记。
enqueueMessage
然后看看MessageQueue如何enqueueMessage。
final boolean enqueueMessage(Message msg,long when){ if (msg.isInUse()){ 抛出 新的 AndroidRuntimeException(msg +“此消息已被使用。” ); } if(msg.target == null){ 抛出新的AndroidRuntimeException(“Message must have a target。”); } boolean needWake; synchronized(this ){ if (mQuiting){ RuntimeException e = new RuntimeException( msg.target +“将消息发送给死线程上的处理程序” ); Log.w( “MessageQueue” ,e.getMessage(),e); 返回 false ; } msg.when = when; 消息p = mMessages; 如果(p == null || when == 0 || when < p.when){ // 新头,如果阻塞则唤醒事件队列。 msg.next = p; mMessages = 味精; needWake = mBlocked; } else { // 插入队列中间。通常我们没有醒来 // 了事件队列,除非是在队列的头一个障碍 // 和消息队列中最早的异步消息。 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 消息上一页; for (;;){ prev = p; p = p.next; if(p == null || when < p.when){ break ; } if(needWake && p.isAsynchronous()){ needWake = false ; } } msg.next = p; // invariant:p == prev.next prev.next = msg; } } if (needWake){ nativeWake(mPtr); } 返回 true ; }
注意代码为红色,当msg.target为null时直接抛出异常。
enqueueMessage中的第一个判断是,如果当前消息队列为空,执行时间或添加新消息的时间为0,或者添加的消息比消息队列头执行时间的执行时间早,则消息是添加到消息队列头(消息队列根据时间顺序),或者找到合适的位置将当前消息添加到消息队列中。
本地发送消息
消息模型不仅可以使用Java层,Native层也可以使用,前面还看到消息队列的初始化也初始化了Looper和NativeMessageQueue Native层,所以Native层也应该可以发送消息。与Java层不同的是,Native层是Looper消息,所有的发送方法都被称为sendMessageAtTime:
void Looper :: sendMessageAtTime(nsecs_t uptime,const sp <MessageHandler>&handler, const Message&message){ #if DEBUG_CALLBACKS ALOGD(“ %p〜sendMessageAtTime - uptime =%lld,handler =%p,what =%d ” , this,正常运行时间,处理程序。得到(),message.what); #万一 size_t i = 0 ; { // 获取锁定 AutoMutex _l(mLock); size_t messageCount = mMessageEnvelopes.size(); while(i <messageCount && uptime> = mMessageEnvelopes.itemAt(i).uptime){ i + = 1 ; } MessageEnvelope messageEnvelope(正常运行时间,处理程序,消息); mMessageEnvelopes.insertAt(messageEnvelope,i,1 ); // 优化:如果活套正在发送消息,然后我们可以跳过 // 调用唤醒(),因为接下来的事情后处理弯针会做 // 消息是决定下一次唤醒时间应该是。事实上, // 这个代码是否在Looper线程上运行都无关紧要。 如果(mSendingMessage){ return ; } } // 释放锁定 // 仅当我们在头部输入新消息时才唤醒轮询循环。 if(i == 0 ){ 唤醒(); } }
Native消息只有一个int类型用于区分不同消息的字段,SendMessageAtTime消息被指定,Message消息的执行时间为,并处理消息Handler:MessageHandler,然后使用MessageEnvelope封装的MessageHandler和Message,Native消息保存到mMessageEnvelopes,MMessageEnvelopes是一个Vector <MessageEnvelope>。Native消息也根据时间顺序,并且Java层信息分别存储在两个队列中。
消息循环
消息队列初始化不错,也知道如何发送消息,以下是如何处理消息,请参阅Handler.loop函数:
public static void loop(){ final Looper me = myLooper(); if(me == null ){ throw new RuntimeException(“No Looper; Looper.prepare()not this on this thread。” ); } 最终的 MessageQueue队列= me.mQueue; // 确保该线程的身份是当地的过程中, // 和跟踪是什么身份令牌实际上是。 Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;){ 消息msg = queue.next(); // 可能会阻止 if(msg == null){ //没有消息表明消息队列正在退出。 返回; } // 这必须在本地变量中,以防UI事件设置记录器 Printer logging = me.mLogging; if(logging!= null ){ logging.println( “>>>>>调度到”+ msg.target +“”+ msg.callback +“:”+ msg.what); } msg.target.dispatchMessage(MSG); if(logging!= null ){ logging.println( “<<<<<已完成”+ msg.target +“”+ msg.callback); } // 确保在调度过程中 // 线程的身份没有被破坏。 final long newIdent = Binder.clearCallingIdentity(); if(ident!= newIdent){ Log.wtf(TAG, “线程身份已从0x更改” + Long.toHexString(ident)+“to 0x” + Long.toHexString(newIdent)+“,同时调度到” + msg.target.getClass()。getName()+“” + msg.callback +“what =”+ msg.what); } msg.recycle(); } }
当循环从MessageQueue接收消息时,调用msg.target.dispatchMessage(MSG),处理程序和消息关联的目标是发送消息,以便熟悉的dispatchMessage调用,消息被回收处理。当queue.next返回null时会退出消息循环,然后看MessageQueue.next是如何去除消息的,并在返回null时。
final Message next(){ int pendingIdleHandlerCount = -1; // -1仅在第一次迭代期间 int nextPollTimeoutMillis = 0 ; for (;;){ if(nextPollTimeoutMillis!= 0 ){ Binder.flushPendingCommands(); } nativePollOnce(mPtr,nextPollTimeoutMillis); synchronized(this ){ if(mQuiting){ 返回null; } // 尝试检索下一条消息。如果找到则返回。 最后 长时间 = SystemClock.uptimeMillis(); 消息prevMsg = null ; 消息msg = mMessages; 如果(msg!= null && msg.target == null ){ // 被障碍阻挡。在队列中查找下一个异步消息。 做{ prevMsg = msg; msg = msg.next; } while(msg!= null &&!msg.isAsynchronous()); } 如果(msg!= null ){ if(now < msg.when){ // 下一条消息没有准备好。设置超时以在准备就绪时唤醒。 nextPollTimeoutMillis =(int)Math.min(msg.when - now,Integer.MAX_VALUE); } 其他{ // 得到一个消息。 mBlocked = false ; if(prevMsg!= null ){ prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null ; if(false)Log.v(“MessageQueue”,“Returning message:”+ msg); msg.markInUse(); 返回味精; } } 其他{ // 没有更多的消息。 nextPollTimeoutMillis = -1 ; } // 如果第一次闲置,然后获得闲置号码的运行。 // 如果队列是空的,或者如果第一消息空闲仅处理运行 // 在队列中(可能是一个障碍)是由于在未来进行处理。 如果(pendingIdleHandlerCount <0 &&(mMessages == null || now < mMessages.when)){ pendingIdleHandlerCount = mIdleHandlers.size(); } 如果(pendingIdleHandlerCount <= 0 ){ // 没有空闲处理程序运行。循环并等待更多。 mBlocked = true ; 继续; } if(mPendingIdleHandlers == null ){ mPendingIdleHandlers = new IdleHandler [Math.max(pendingIdleHandlerCount,4 )]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // 运行闲置处理程序。 // 在第一次迭代期间,我们只能到达这个代码块。 for(int i = 0; i <pendingIdleHandlerCount; i ++ ){ final IdleHandler idler = mPendingIdleHandlers [i]; mPendingIdleHandlers [i] = null ; // 释放对处理程序的引用 boolean keep = false ; 尝试{ keep = idler.queueIdle(); } catch (Throwable t){ Log.wtf( “MessageQueue”,“IdleHandler抛出异常” ,t); } 如果(!keep){ synchronized(this ){ mIdleHandlers.remove(惰轮); } } } // 将空闲处理程序计数重置为0,以便我们不再运行它们。 pendingIdleHandlerCount = 0 ; // 在调用闲置处理程序时,可能会传递一条新消息 // 因此请返回,然后再等待一条待处理消息。 nextPollTimeoutMillis = 0 ; } }
MessageQueue.next将首先调用nativePollOnce,如果mQuiting true返回null,则Looper将退出消息循环。
消息队列消息的下一个头部,如果头部消息是Barrier(target == null),则在下次遍历中查找第一个异步消息时,对消息的下一个测试访问(消息队列消息或第一个异步消息),如果NULL表示没有消息要执行,则设置nextPollTimeoutMillis = -1; 否则检测到这个消息到执行时间。如果markInUse的执行时间和消息从消息队列中删除,然后从下一个循环返回; 否则,设置nextPollTimeoutMillis =(int)Math.min(msg.when - now,Integer.MAX_VALUE),即距离最近的执行消息还需要多长时间,当前消息队列中没有消息可以执行(设置Barrier并且不设置异步消息或消息队列为空)或消息队列的头部不到执行时间,
首先看nativePollOnce,native方法,叫做JNI,最后调用到Native Looper :: pollOnce,并从Java层传递到nextPollTimeMillis,即在消息队列中执行Java层的时间,以使近期消息的执行时间有多长。
int Looper :: pollOnce(int timeoutMillis,int * outFd,int * outEvents,void ** outData){ int result = 0 ; for (;;){ 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 - 返回信号标识符%d:” “ fd =%d,events = 0x%x,data =%p ” , this ,ident,fd,events,data); #endif if(outFd!= NULL)* outFd = fd; if(outEvents!= NULL)* outEvents = events; 如果(outData!= NULL)* outData = data; 返回标识; } } 如果(结果!= 0 ){ #if DEBUG_POLL_AND_WAKE ALOGD(“ %p〜pollOnce - 返回结果%d ”,this ,result); #endif if(outFd!= NULL)* outFd = 0 ; if(outEvents!= NULL)* outEvents = 0 ; 如果(outData!= NULL)* outData = NULL; 返回结果; } result = pollInner(timeoutMillis); } }
先看不到一堆代码,看看pollInner:
int Looper :: pollInner(int timeoutMillis){ #if DEBUG_POLL_AND_WAKE ALOGD(“ %p〜pollOnce - waiting:timeoutMillis =%d ”,this ,timeoutMillis); #万一 // 根据下一封邮件何时到期来调整超时时间。 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 - %lldns中的下一条消息,调整后的超时值:timeoutMillis =%d ” , this,mNextMessageUptime - now,timeoutMillis); #万一 } // 投票。 int result = ALOOPER_POLL_WAKE; mResponses.clear(); mResponseIndex = 0 ; struct epoll_event eventItems [EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis); // 获取锁定。 m锁。lock (); // 检查轮询错误。 if(eventCount < 0 ){ if(errno == EINTR){ goto Done; } ALOGW(“ Poll失败,出现意外错误,errno =%d ” ,errno); 结果 = ALOOPER_POLL_ERROR; 转到完成; } // 检查轮询超时。 if(eventCount == 0 ){ #if DEBUG_POLL_AND_WAKE ALOGD(“ %p〜pollOnce - timeout ”,this ); #endif 结果 = ALOOPER_POLL_TIMEOUT; 转到完成; } // 处理所有事件。 #if DEBUG_POLL_AND_WAKE ALOGD(“ %p〜pollOnce - 处理来自%d个fds的事件”,this ,eventCount); #万一 for(int i = 0 ; i <eventCount; i ++ ){ int fd = eventItems [i] .data.fd; uint32_t epollEvents = eventItems [i] .events; if(fd == mWakeReadPipeFd){ if(epollEvents&EPOLLIN){ 苏醒(); } else { ALOGW(“ 在读取管道时忽略意外的epoll事件0x%x ” ,epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd); if(requestIndex> = 0 ){ int events = 0 ; if(epollEvents&EPOLLIN)events | = ALOOPER_EVENT_INPUT; if(epollEvents&EPOLLOUT)events | = ALOOPER_EVENT_OUTPUT; if(epollEvents&EPOLLERR)事件| = ALOOPER_EVENT_ERROR; if(epollEvents&EPOLLHUP)events | = ALOOPER_EVENT_HANGUP; pushResponse(events,mRequests.valueAt(requestIndex)); } else { ALOGW( “ 忽略突发事件epoll的为0x%X上的fd%d是” “ 不再注册。” ,epollEvents,FD); } } } 完成:; // 调用未决的消息回调。 mNextMessageUptime = LLONG_MAX; while(mMessageEnvelopes.size()!= 0 ){ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); const MessageEnvelope&messageEnvelope = mMessageEnvelopes.itemAt(0 ); 如果(messageEnvelope.uptime <= now){ // 从列表中删除信封。 // 我们保持对处理程序的强引用,直到调用handleMessage // 完成。然后我们放下它,以便可以在*之前删除处理程序* // 我们重新获取锁定。 { // 获取处理程序 sp <MessageHandler> handler = messageEnvelope.handler; 消息消息 = messageEnvelope.message; mMessageEnvelopes.removeAt(0 ); mSendingMessage = true ; mLock.unlock(); #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD( “ %P〜pollOnce -发送消息:处理程序=%P,什么=%d ” , 这个,处理程序获得(),message.what); #endif handler - > handleMessage(message); } // 发布处理程序 mLock。lock (); mSendingMessage = false ; 结果 = ALOOPER_POLL_CALLBACK; } else { // 队列头部的最后一条消息决定了下一个唤醒时间。 mNextMessageUptime = messageEnvelope.uptime; 休息; } } // 释放锁定。 mLock.unlock(); // 调用所有响应回调。 for(size_t i = 0 ; i <mResponses.size(); i ++ ){ 回应与回应= mResponses.editItemAt(i); if(response.request.ident == ALOOPER_POLL_CALLBACK){ int fd = response.request.fd; int events = response.events; void * data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD( “ %P〜pollOnce -调用FD事件回调%P:FD =%d,事件=为0x%X,数据=%P ” , 这,response.request.callback 得到(),FD,事件,数据); #endif intcallbackResult = response.request.callback-> handleEvent(fd,events,data); if(callbackResult == 0 ){ removeFd(FD); } //立即清除响应结构中的回调引用,因为 // 直到下一次轮询之前 // // 不会清除响应向量本身。 response.request.callback.clear(); 结果 = ALOOPER_POLL_CALLBACK; } } 返回结果; }
Java消息存储在mMessages的MessageQueue成员的Java层中,Native消息存储在Native Looper mMessageEnvelopes中,可以说有两个消息队列,并且按时间排列。TimeOutMillis表示Java层下一步执行消息执行多久,mNextMessageUpdate表示Native层下一步执行消息需要多长时间,如果timeOutMillis为0,epoll_wait不会设置TimeOut直接返回; 如果-1 Java层没有消息,直接用Native时间超时; 最小值或pollInner将两个值作为timeOut调用epoll_wait。当epoll_wait返回时可能有以下情况:
错误返回。
时间到
正常的返回,有事件产生的描述符。
如果前者直接转到了两个。
否则,FD发生了,如果mWakeReadPipeFd EPOLLIN事件唤醒被调用,如果没有mWakeReadPipeFd,它是通过addFD添加FD,在addFD中,将FD和events,callback,数据包放入Request对象中,以FD为key存储在KeyedVector mRequests中,所以这里FD在addFD Request中获取关联关系,并且通过添加pushResonse mResonse队列(Vector),Resonse仅用于事件和Request包。如果出现epoll_wait或超时错误,则没有事件描述符,不执行此代码,所以直接跳转到DONE。
void Looper :: pushResponse(int events,const Request&request){ 响应响应; response.events = events; response.request = 请求; mResponses.push(响应); }
接下来输入DONE,从mMessageEnvelopes中删除Native消息头,如果在执行时到达handleMessage被调用来处理它在内部存储的MessageeHandler并从Native消息队列中移除,则将结果设置为ALOOPER_POLL_CALLBACK,否则计算mNextMessageUptime表示Native消息队列消息执行时间处理时间。如果不是头部消息执行时间可能是Java层消息队列消息执行时间小于Native层消息队列消息执行时间,到达epoll_wait TimeOut Java消息的执行时间,或者通过addFd描述符添加了epoll_wait返回时发生,或epoll_wait错误返回。本机消息不是障碍和异步。
最后,通过mResponses(只需通过pushResponse放入它),如果response.request.ident = = ALOOPER_POLL_CALLBACK,我们调用注册回调handleEvent(FD,events,data)处理,然后从mResonses队列中移除,之后遍历,MResponses保留并且ident> = 0,回调为NULL。在NativeMessageQueue初始化Looper时,将mAllowNonCallbacks设为false,因此mResponses之后的处理必须为空。
然后返回到pollOnce。PollOnce是for循环,pollInner处理所有response.request.ident == ALOOPER_POLL_CALLBACK响应,如果mResponses不为空以查找ident> 0响应,则进入for循环的第二个响应,该函数调用pollOnce返回的值为ident自己处理,这里我们在NativeMessageQueue中调用Loope pollOnce,没有返回处理,但是mAllowNonCallbacks false不可能进入循环。pollInner的返回值可能不是0,也可能只是负值,因此循环的pollOnce将只执行两次,然后再次返回。
Native Looper可以单独使用,也可以具有准备功能,那么mAllowNonCallbakcs值可能为true,mResponses中的pollOnce有意义。
苏醒并醒来
在Native Looper构造函数中,管道打开了一条管道,结合使用mWakeReadPipeFd和mWakeWritePipeFd保存了管道端的读写端,然后使用epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem)监视EPOLLIN事件的读取结束,在pollInner中通过epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis)读取事件,写入mWakeWritePipeFd时,以及在什么时候读取mWakeReadPipeFd。
在Looper.cpp中,我们可以在下面找到两个函数:
void Looper :: wake(){ #if DEBUG_POLL_AND_WAKE ALOGD(“ %p〜wake ”,this ); #万一 ssize_t nWrite; 做{ nWrite =写入(mWakeWritePipeFd,“ W ”,1 ); } while(nWrite == - 1 && errno == EINTR); if(nWrite!= 1 ){ if(errno!= EAGAIN){ ALOGW(“ 不能写唤醒信号,errno =%d ” ,errno); } } } void Looper :: awoken (){ #if DEBUG_POLL_AND_WAKE ALOGD(“ %p〜awoken ”,this ); #万一 字符缓冲区[ 16 ]; ssize_t nRead; 做{ nRead = read(mWakeReadPipeFd,buffer,sizeof (buffer)); } while((nRead == - 1 && errno == EINTR)|| nRead == sizeof (buffer)); }
唤醒函数写入一个“mWakeWritePipeFd”; W“字符,从mWakeReadPipeFd中唤醒,以mWakeWritePipeFd向epoll_wait中的pollInner写入数据,您可以监听事件返回。pollInner还可以查看mWakeReadPipeFd EPOLLIN事件是否被称为唤醒消耗字符回写处理。
当唤醒唤醒?只需找到一个呼叫线上的地方,看看Looper.cpp,在sendMessageAtTime发送Native Message时,应根据mMessageEnvelopes的执行时间插入发送消息,如果是在头插入我们调用唤醒唤醒epoll_wait,因为在pollInner中根据执行时间和Native层消息队列头消息的执行时间计算Java层消息队列头中的超时值,如果新消息插入头中,则执行至少两个消息的时间在一个之前,所以你应该唤醒epoll_wait,Epoll_wait返回,检查Native消息队列,查看头部消息是否只是插入消息到执行时间,要执行,否则你可能需要设置新的超时。
如果(p == null || when == 0 || when < p.when){ // 新头,如果阻塞则唤醒事件队列。 msg.next = p; mMessages = 味精; needWake = mBlocked; } else { // 插入队列中间。通常我们没有醒来 // 了事件队列,除非是在队列的头一个障碍 // 和消息队列中最早的异步消息。 needWake = mBlocked && p.target == null && msg.isAsynchronous(); // 如果头部是Barrier并且新消息是异步消息,则可能需要唤醒; 消息上一页; for (;;){ prev = p; p = p.next; if(p == null || when < p.when){ break ; } if(needWake && p.isAsynchronous()){ // 消息队列是异步消息,并且消息之前的执行时间在消息中,所以不需要唤醒。 needWake = false ; } } msg.next = p; // invariant:p == prev.next prev.next = msg; }
在头插入消息时不必调用nativeWake,因为之前可能会执行IdleHandler,如果执行IdleHandler,则在IdleHandler执行下一个PollTimeoutMillis时将其设置为0,下次启动for循环为0调用nativePollOnce,不需要唤醒,只有在没有消息可以执行的情况下(消息队列为空或不在执行时间内)并且不将IdleHandler mBlocked设置为true。
如果消息队列的Java层是Barrier Block并且插入是异步消息,则可能需要唤醒Looper,因为异步消息可以在Barrier中执行,但异步消息必须在第一时间执行异步消息。
退出Looper也需要唤醒,removeSyncBarrier也可能需要。