Android消息处理机制HandlerLooperMessageQueue与Message.docx
《Android消息处理机制HandlerLooperMessageQueue与Message.docx》由会员分享,可在线阅读,更多相关《Android消息处理机制HandlerLooperMessageQueue与Message.docx(29页珍藏版)》请在冰豆网上搜索。
![Android消息处理机制HandlerLooperMessageQueue与Message.docx](https://file1.bdocx.com/fileroot1/2023-1/4/184c4cdd-f33f-4a78-897e-26c88bb69757/184c4cdd-f33f-4a78-897e-26c88bb697571.gif)
Android消息处理机制HandlerLooperMessageQueue与Message
Android是消息驱动的,实现消息驱动有几个要素:
消息的表示:
Message
消息队列:
MessageQueue
消息循环,用于循环取出消息进行处理:
Looper
消息处理,消息循环从消息队列中取出消息后要对消息进行处理:
Handler
平时我们最常使用的就是Message与Handler了,如果使用过HandlerThread或者自己实现类似HandlerThread的东西可能还会接触到Looper,而MessageQueue是Looper内部使用的,对于标准的SDK,我们是无法实例化并使用的(构造函数是包可见性)。
我们平时接触到的Looper、Message、Handler都是用JAVA实现的,Android做为基于Linux的系统,底层用C、C++实现的,而且还有NDK的存在,消息驱动的模型怎么可能只存在于JAVA层,实际上,在Native层存在与Java层对应的类如Looper、MessageQueue等。
初始化消息队列
首先来看一下如果一个线程想实现消息循环应该怎么做,以HandlerThread为例:
复制代码
publicvoidrun(){
mTid=Process.myTid();
Looper.prepare();
synchronized(this){
mLooper=Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid=-1;
}
复制代码
主要是红色标明的两句,首先调用prepare初始化MessageQueue与Looper,然后调用loop进入消息循环。
先看一下Looper.prepare。
复制代码
publicstaticvoidprepare(){
prepare(true);
}
privatestaticvoidprepare(booleanquitAllowed){
if(sThreadLocal.get()!
=null){
thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");
}
sThreadLocal.set(newLooper(quitAllowed));
}
复制代码
重载函数,quitAllowed默认为true,从名字可以看出来就是消息循环是否可以退出,默认是可退出的,Main线程(UI线程)初始化消息循环时会调用prepareMainLooper,传进去的是false。
使用了ThreadLocal,每个线程可以初始化一个Looper。
再来看一下Looper在初始化时都做了什么:
复制代码
privateLooper(booleanquitAllowed){
mQueue=newMessageQueue(quitAllowed);
mRun=true;
mThread=Thread.currentThread();
}
MessageQueue(booleanquitAllowed){
mQuitAllowed=quitAllowed;
nativeInit();
}
复制代码
在Looper初始化时,新建了一个MessageQueue的对象保存了在成员mQueue中。
MessageQueue的构造函数是包可见性,所以我们是无法直接使用的,在MessageQueue初始化的时候调用了nativeInit,这是一个Native方法:
复制代码
staticvoidandroid_os_MessageQueue_nativeInit(JNIEnv*env,jobjectobj){
NativeMessageQueue*nativeMessageQueue=newNativeMessageQueue();
if(!
nativeMessageQueue){
jniThrowRuntimeException(env,"Unabletoallocatenativequeue");
return;
}
nativeMessageQueue->incStrong(env);
android_os_MessageQueue_setNativeMessageQueue(env,obj,nativeMessageQueue);
}
staticvoidandroid_os_MessageQueue_setNativeMessageQueue(JNIEnv*env,jobjectmessageQueueObj,
NativeMessageQueue*nativeMessageQueue){
env->SetIntField(messageQueueObj,gMessageQueueClassInfo.mPtr,
reinterpret_cast(nativeMessageQueue));
}
复制代码
在nativeInit中,new了一个Native层的MessageQueue的对象,并将其地址保存在了Java层MessageQueue的成员mPtr中,Android中有好多这样的实现,一个类在Java层与Native层都有实现,通过JNI的GetFieldID与SetIntField把Native层的类的实例地址保存到Java层类的实例的mPtr成员中,比如Parcel。
再看NativeMessageQueue的实现:
复制代码
NativeMessageQueue:
:
NativeMessageQueue():
mInCallback(false),mExceptionObj(NULL){
mLooper=Looper:
:
getForThread();
if(mLooper==NULL){
mLooper=newLooper(false);
Looper:
:
setForThread(mLooper);
}
}
复制代码
在NativeMessageQueue的构造函数中获得了一个Native层的Looper对象,Native层的Looper也使用了线程本地存储,注意newLooper时传入了参数false。
复制代码
Looper:
:
Looper(boolallowNonCallbacks):
mAllowNonCallbacks(allowNonCallbacks),mSendingMessage(false),
mResponseIndex(0),mNextMessageUptime(LLONG_MAX){
intwakeFds[2];
intresult=pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result!
=0,"Couldnotcreatewakepipe.errno=%d",errno);
mWakeReadPipeFd=wakeFds[0];
mWakeWritePipeFd=wakeFds[1];
result=fcntl(mWakeReadPipeFd,F_SETFL,O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result!
=0,"Couldnotmakewakereadpipenon-blocking.errno=%d",
errno);
result=fcntl(mWakeWritePipeFd,F_SETFL,O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result!
=0,"Couldnotmakewakewritepipenon-blocking.errno=%d",
errno);
//Allocatetheepollinstanceandregisterthewakepipe.
mEpollFd=epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd<0,"Couldnotcreateepollinstance.errno=%d",errno);
structepoll_eventeventItem;
memset(&eventItem,0,sizeof(epoll_event));//zerooutunusedmembersofdatafieldunion
eventItem.events=EPOLLIN;
eventItem.data.fd=mWakeReadPipeFd;
result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);
LOG_ALWAYS_FATAL_IF(result!
=0,"Couldnotaddwakereadpipetoepollinstance.errno=%d",
errno);
}
复制代码
Native层的Looper使用了epoll。
初始化了一个管道,用mWakeWritePipeFd与mWakeReadPipeFd分别保存了管道的写端与读端,并监听了读端的EPOLLIN事件。
注意下初始化列表的值,mAllowNonCallbacks的值为false。
mAllowNonCallback是做什么的?
使用epoll仅为了监听mWakeReadPipeFd的事件?
其实NativeLooper不仅可以监听这一个描述符,Looper还提供了addFd方法:
intaddFd(intfd,intident,intevents,ALooper_callbackFunccallback,void*data);
intaddFd(intfd,intident,intevents,constsp&callback,void*data);
fd表示要监听的描述符。
ident表示要监听的事件的标识,值必须>=0或者为ALOOPER_POLL_CALLBACK(-2),event表示要监听的事件,callback是事件发生时的回调函数,mAllowNonCallbacks的作用就在于此,当mAllowNonCallbacks为true时允许callback为NULL,在pollOnce中ident作为结果返回,否则不允许callback为空,当callback不为NULL时,ident的值会被忽略。
还是直接看代码方便理解:
复制代码
intLooper:
:
addFd(intfd,intident,intevents,constsp&callback,void*data){
#ifDEBUG_CALLBACKS
ALOGD("%p~addFd-fd=%d,ident=%d,events=0x%x,callback=%p,data=%p",this,fd,ident,
events,callback.get(),data);
#endif
if(!
callback.get()){
if(!
mAllowNonCallbacks){
ALOGE("InvalidattempttosetNULLcallbackbutnotallowedforthislooper.");
return-1;
}
if(ident<0){
ALOGE("InvalidattempttosetNULLcallbackwithident<0.");
return-1;
}
}else{
ident=ALOOPER_POLL_CALLBACK;
}
intepollEvents=0;
if(events&ALOOPER_EVENT_INPUT)epollEvents|=EPOLLIN;
if(events&ALOOPER_EVENT_OUTPUT)epollEvents|=EPOLLOUT;
{//acquirelock
AutoMutex_l(mLock);
Requestrequest;
request.fd=fd;
request.ident=ident;
request.callback=callback;
request.data=data;
structepoll_eventeventItem;
memset(&eventItem,0,sizeof(epoll_event));//zerooutunusedmembersofdatafieldunion
eventItem.events=epollEvents;
eventItem.data.fd=fd;
ssize_trequestIndex=mRequests.indexOfKey(fd);
if(requestIndex<0){
intepollResult=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,fd,&eventItem);
if(epollResult<0){
ALOGE("Erroraddingepolleventsforfd%d,errno=%d",fd,errno);
return-1;
}
mRequests.add(fd,request);
}else{
intepollResult=epoll_ctl(mEpollFd,EPOLL_CTL_MOD,fd,&eventItem);
if(epollResult<0){
ALOGE("Errormodifyingepolleventsforfd%d,errno=%d",fd,errno);
return-1;
}
mRequests.replaceValueAt(requestIndex,request);
}
}//releaselock
return1;
}
复制代码
如果callback为空会检查mAllowNonCallbacks看是否允许callback为空,如果允许callback为空还会检测ident是否>=0。
如果callback不为空会把ident的值赋值为ALOOPER_POLL_CALLBACK,不管传进来的是什么值。
接下来把传进来的参数值封装到一个Request结构体中,并以描述符为键保存到一个KeyedVectormRequests中,然后通过epoll_ctl添加或替换(如果这个描述符之前有调用addFD添加监听)对这个描述符事件的监听。
类图:
发送消息
通过Looper.prepare初始化好消息队列后就可以调用Looper.loop进入消息循环了,然后我们就可以向消息队列发送消息,消息循环就会取出消息进行处理,在看消息处理之前,先看一下消息是怎么被添加到消息队列的。
在Java层,Message类表示一个消息对象,要发送消息首先就要先获得一个消息对象,Message类的构造函数是public的,但是不建议直接newMessage,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后系统放入缓存池,会占用很多内存的,如下所示:
复制代码
publicstaticMessageobtain(){
synchronized(sPoolSync){
if(sPool!
=null){
Messagem=sPool;
sPool=m.next;
m.next=null;
sPoolSize--;
returnm;
}
}
returnnewMessage();
}
publicvoidrecycle(){
clearForRecycle();
synchronized(sPoolSync){
if(sPoolSizenext=sPool;
sPool=this;
sPoolSize++;
}
}
}
复制代码
Message内部通过next成员实现了一个链表,这样sPool就了为了一个Messages的缓存链表。
消息对象获取到了怎么发送呢,大家都知道是通过Handler的post、sendMessage等方法,其实这些方法最终都是调用的同一个方法sendMessageAtTime:
复制代码
publicbooleansendMessageAtTime(Messagemsg,longuptimeMillis){
MessageQueuequeue=mQueue;
if(queue==null){
RuntimeExceptione=newRuntimeException(
this+"sendMessageAtTime()calledwithnomQueue");
Log.w("Looper",e.getMessage(),e);
returnfalse;
}
returnenqueueMessage(queue,msg,uptimeMillis);
}
复制代码
sendMessageAtTime获取到消息队列然后调用enqueueMessage方法,消息队列mQueue是从与Handler关联的Looper获得的。
复制代码
privatebooleanenqueueMessage(MessageQueuequeue,Messagemsg,longuptimeMillis){
msg.target=this;
if(mAsynchronous){
msg.setAsynchronous(true);
}
returnqueue.enqueueMessage(msg,uptimeMillis);
}
复制代码
enqueueMessage将message的target设置为当前的handler,然后调用MessageQueue的enqueueMessage,在调用queue.enqueueMessage之前判断了mAsynchronous,从名字看是异步消息的意思,要明白Asynchronous的作用,需要先了解一个概念Barrier。
Barrier与AsynchronousMessage
Barrier是什么意思呢,从名字看是一个拦截器,在这个拦截器后面的消息都暂时无法执行,直到这个拦截器被移除了,MessageQueue有一个函数叫enqueueSyncBarier可以添加一个Barrier。
复制代码
intenqueueSyncBarrier(longwhen){
//Enqueueanewsyncbarriertoken.
//Wedon'tneedtowakethequeuebecausethepurposeofabarrieristostallit.
synchronized(this){
finalinttoken=mNextBarrierToken++;
finalMessagemsg=Message.obtain();
msg.arg1=token;
Messageprev=null;
Messagep=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=msg;
}
returntoken;
}
}
复制代码
在enqueueSyncBarrier中,obtain了一个Message,并设置msg.arg1=token,token仅是一个每次调用enqueueSyncBarrier时自增的int值,目的是每次调用enqueueSyncBarrier时返回唯一的一个token,这个Message同样需要设置执行时间,然后插入到消息队列,特殊的是这个Message没有设置target,即msg.target为null。
进入消息循环后会不停地从MessageQueue中取消息执行,调用的是MessageQueue的next函数,其中有这么一段:
复制代码
Messagemsg=mMessages;
if(msg!
=null&&msg.target==null){
//Stalledbyabarrier.Findthenextasynchronousmessageinthequeue.
do{
prevMsg=msg;
msg=msg.next;
}while(msg!
=null&&!
msg.isAsynchronous());
}
复制代码
如果队列头部的消息的target为null就表示它是个Barrier,因为只有两种方法往mMessages中添加消息,一种是enqueueMessage,另一种是enqueueBarrier,而enqueueMessage中如果mst.target为null是直接抛异常的,后面会看到。
所谓的异步消息其实就是这样的,我们可以通过enqueueBarrier往消息队列中插入一个Barrier,那么队列中执行时间在这个Barrier以后的同步消息都会被这个Barrier拦截住无法执行,直到我们调用removeBarrier移除了这个Barrier,而异步消息则没有影响,消息默认就是同步消息,除非我们调用了Message的setAsynchronous,这个方法是隐藏的。
只有在初始化Handler时通过参数指定往这个Handler发送的消息都是异步的,这样在Han