Android消息处理机制HandlerLooperMessageQueue与Message.docx

上传人:b****6 文档编号:6166647 上传时间:2023-01-04 格式:DOCX 页数:29 大小:68.43KB
下载 相关 举报
Android消息处理机制HandlerLooperMessageQueue与Message.docx_第1页
第1页 / 共29页
Android消息处理机制HandlerLooperMessageQueue与Message.docx_第2页
第2页 / 共29页
Android消息处理机制HandlerLooperMessageQueue与Message.docx_第3页
第3页 / 共29页
Android消息处理机制HandlerLooperMessageQueue与Message.docx_第4页
第4页 / 共29页
Android消息处理机制HandlerLooperMessageQueue与Message.docx_第5页
第5页 / 共29页
点击查看更多>>
下载资源
资源描述

Android消息处理机制HandlerLooperMessageQueue与Message.docx

《Android消息处理机制HandlerLooperMessageQueue与Message.docx》由会员分享,可在线阅读,更多相关《Android消息处理机制HandlerLooperMessageQueue与Message.docx(29页珍藏版)》请在冰豆网上搜索。

Android消息处理机制HandlerLooperMessageQueue与Message.docx

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(sPoolSize

next=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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1