【移动应用开发技术】Android Handler 消息机制原理解析_第1页
【移动应用开发技术】Android Handler 消息机制原理解析_第2页
【移动应用开发技术】Android Handler 消息机制原理解析_第3页
【移动应用开发技术】Android Handler 消息机制原理解析_第4页
【移动应用开发技术】Android Handler 消息机制原理解析_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】AndroidHandler消息机制原理解析

欢迎访问我的个人博客传送门

欢迎访问我的个人博客传送门前言做过Android开发的童鞋都知道,不能在非主线程修改UI控件,因为Android规定只能在主线程中访问UI,如果在子线程中访问UI,那么程序就会抛出异常android.view.ViewRootImpl$CalledFromWrongThreadException:Onlytheoriginalthreadthatcreatedaviewhierarchy.并且,Android也不建议在UI线程既主线程中做一些耗时操作,否则会导致程序ANR。如果我们需要做一些耗时的操作并且操作结束后要修改UI,那么就需要用到Android提供的Handler切换到主线程来访问UI。因此,系统之所以提供Handler,主要原因就是为了解决在子线程中无法访问UI的问题。概述要理解Handler消息机制原理还需要了解几个概念:UI线程主线程ActivityThreadMessageHandler发送和处理的消息,由MessageQueue管理。MessageQueue消息队列,用来存放通过Handler发送的消息,按照先进先出执行,内部使用的是单链表的结构。Handler负责发送消息和处理消息。Looper负责消息循环,循环取出MessageQueue里面的Message,并交给相应的Handler进行处理。在应用启动时,会开启一个UI线程,并且启动消息循环,应用不停地从该消息列表中取出、处理消息达到程序运行的效果。Looper负责的就是创建一个MessageQueue,然后进入一个无限循环体不断从该MessageQueue中读取消息,而消息的创建者就是一个或多个Handler。流程图如下:下面结合源码来具体分析LooperLooper比较重要的两个方法是prepare()和loop()先看下构造方法finalMessageQueuemQueue;

privateLooper(booleanquitAllowed){

mQueue=newMessageQueue(quitAllowed);

mThread=Thread.currentThread();

}Looper在创建时会新建一个MessageQueue通过prepare方法可以为Handler创建一个Lopper,源码如下:staticfinalThreadLocal<Looper>sThreadLocal=newThreadLocal<Looper>();

privatestaticLoopersMainLooper;//guardedbyLooper.class

publicstaticvoidprepare(){

prepare(true);

}

privatestaticvoidprepare(booleanquitAllowed){

if(sThreadLocal.get()!=null){

//一个线程只能有一个looper

thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");

}

sThreadLocal.set(newLooper(quitAllowed));

}

publicstaticvoidprepareMainLooper(){

prepare(false);

synchronized(Looper.class){

if(sMainLooper!=null){

thrownewIllegalStateException("ThemainLooperhasalreadybeenprepared.");

}

sMainLooper=myLooper();

}

}可以看到这里创建的Looper对象使用ThreadLocal保存,这里简单介绍下ThreadLocal。ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据,这样就保证了一个线程对应了一个Looper,从源码中也可以看出一个线程也只能有一个Looper,否则就会抛出异常。prepareMainLooper()方法是系统在ActivityThread中调用的。ActivityThread.javapublicstaticvoidmain(String[]args){

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"ActivityThreadMain");

SamplingProfilerIntegration.start();

//...省略代码

Looper.prepareMainLooper();

ActivityThreadthread=newActivityThread();

thread.attach(false);

if(sMainThreadHandler==null){

sMainThreadHandler=thread.getHandler();

}

if(false){

Looper.myLooper().setMessageLogging(new

LogPrinter(Log.DEBUG,"ActivityThread"));

}

//EndofeventActivityThreadMain.

Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

Looper.loop();

thrownewRuntimeException("Mainthreadloopunexpectedlyexited");

}由此可以看出,系统在创建时,会自动创建Looper来处理消息,所以我们一般在主线程中使用Handler时,是不需要手动调用Looper.prepare()的。这样Handler就默认和主线程的Looper绑定。当Handler绑定的Looper是主线程的Looper时,则该Handler可以在其handleMessage中更新UI,否则更新UI则会抛出异常。在开发中,我们可能在多个地方使用Handler,所以又可以得出一个结论:一个Looper可以和多个Handler绑定,那么Looper是怎么区分Message由哪个Handler处理呢?继续看源码loop()publicstaticvoidloop(){

finalLooperme=myLooper();

if(me==null){

thrownewRuntimeException("NoLooper;Looper.prepare()wasn'tcalledonthisthread.");

}

finalMessageQueuequeue=me.mQueue;

//Makesuretheidentityofthisthreadisthatofthelocalprocess,

//andkeeptrackofwhatthatidentitytokenactuallyis.

Binder.clearCallingIdentity();

finallongident=Binder.clearCallingIdentity();

for(;;){

Messagemsg=queue.next();//mightblock

if(msg==null){

//Nomessageindicatesthatthemessagequeueisquitting.

return;

}

//Thismustbeinalocalvariable,incaseaUIeventsetsthelogger

finalPrinterlogging=me.mLogging;

if(logging!=null){

logging.println(">>>>>Dispatchingto"+msg.target+""+

msg.callback+":"+msg.what);

}

finallongslowDispatchThresholdMs=me.mSlowDispatchThresholdMs;

finallongtraceTag=me.mTraceTag;

if(traceTag!=0&&Trace.isTagEnabled(traceTag)){

Trace.traceBegin(traceTag,msg.target.getTraceName(msg));

}

finallongstart=(slowDispatchThresholdMs==0)?0:SystemClock.uptimeMillis();

finallongend;

try{

msg.target.dispatchMessage(msg);

end=(slowDispatchThresholdMs==0)?0:SystemClock.uptimeMillis();

}finally{

if(traceTag!=0){

Trace.traceEnd(traceTag);

}

}

if(slowDispatchThresholdMs>0){

finallongtime=end-start;

if(time>slowDispatchThresholdMs){

Slog.w(TAG,"Dispatchtook"+time+"mson"

+Thread.currentThread().getName()+",h="+

msg.target+"cb="+msg.callback+"msg="+msg.what);

}

}

if(logging!=null){

logging.println("<<<<<Finishedto"+msg.target+""+msg.callback);

}

//Makesurethatduringthecourseofdispatchingthe

//identityofthethreadwasn'tcorrupted.

finallongnewIdent=Binder.clearCallingIdentity();

if(ident!=newIdent){

Log.wtf(TAG,"Threadidentitychangedfrom0x"

+Long.toHexString(ident)+"to0x"

+Long.toHexString(newIdent)+"whiledispatchingto"

+msg.target.getClass().getName()+""

+msg.callback+"what="+msg.what);

}

msg.recycleUnchecked();//回收消息

}

}代码比较多,我们捡重点看。2~6行获取当前Looper如果没有则抛异常,有则获取消息队列MessageQueue所以如果我们在子线程中使用Handler则必须手动调用Looper.prepare()和Looper.loop()系统在代码中提供了示例代码classLooperThreadextendsThread{

publicHandlermHandler;

publicvoidrun(){

Looper.prepare();

mHandler=newHandler(){

publicvoidhandleMessage(Messagemsg){

//processincomingmessageshere

}

};

Looper.loop();

}

}获取到MessageQueue后开启消息循环,不断从MessageQueue中取消息,无则阻塞,等待消息。有则调用msg.target.dispatchMessage(msg)处理消息。msg.target就是Message所属的Handler,这个会再后面具体介绍Handler中会说明所以上面的问题就可以回答了,Looper不需要考虑怎么区分Message由哪个Handler处理,只负责开启消息循环接收消息并处理消息即可。处理完消息后会调用msg.recycleUnchecked()来回收消息。那么开启消息循环后,可以停止吗?答案是肯定的,Looper提供了quit()和quitSafely()来退出。publicvoidquit(){

mQueue.quit(false);

}

publicvoidquitSafely(){

mQueue.quit(true);

}可以看到实际上调用的是MessageQueue中的退出方法,具体会在MessageQueue中介绍。调用quit()会直接退出Looper,而quitSafely()只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出。在Loooper退出后,通过Handler发送消息会失败。如果在子线程中手动创建了Looper,则应在处理完操作后退出Looper终止消息循环。到此Looper的源码分析就完了,我们来总结下Looper所做的工作:Message和MessageQueueMessage是线程通信中传递的消息,它有几个关键点MessageQueueMessageQueue负责管理消息队列,通过一个单链表的数据结构来维护。源码中有三个主要方法:next方法Messagenext(){

//Returnhereifthemessageloophasalreadyquitandbeendisposed.

//Thiscanhappeniftheapplicationtriestorestartalooperafterquit

//whichisnotsupported.

finallongptr=mPtr;

if(ptr==0){

returnnull;

}

intpendingIdleHandlerCount=-1;//-1onlyduringfirstiteration

intnextPollTimeoutMillis=0;

for(;;){

if(nextPollTimeoutMillis!=0){

Binder.flushPendingCommands();

}

nativePollOnce(ptr,nextPollTimeoutMillis);

synchronized(this){

//Trytoretrievethenextmessage.Returniffound.

finallongnow=SystemClock.uptimeMillis();

MessageprevMsg=null;

Messagemsg=mMessages;

if(msg!=null&&msg.target==null){

//Stalledbyabarrier.Findthenextasynchronousmessageinthequeue.

do{

prevMsg=msg;

msg=msg.next;

}while(msg!=null&&!msg.isAsynchronous());

}

if(msg!=null){

if(now<msg.when){

//Nextmessageisnotready.Setatimeouttowakeupwhenitisready.

nextPollTimeoutMillis=(int)Math.min(msg.when-now,Integer.MAX_VALUE);

}else{

//Gotamessage.

mBlocked=false;

if(prevMsg!=null){

prevMsg.next=msg.next;

}else{

mMessages=msg.next;

}

msg.next=null;

if(DEBUG)Log.v(TAG,"Returningmessage:"+msg);

msg.markInUse();

returnmsg;

}

}else{

//Nomoremessages.

nextPollTimeoutMillis=-1;

}

//Processthequitmessagenowthatallpendingmessageshavebeenhandled.

if(mQuitting){

dispose();

returnnull;

}

//...省略代码

}

}

可以发现next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息到来时,next方法会从中获取消息出来返回给Looper去处理,并将其从消息列表中移除。quit方法voidquit(booleansafe){

if(!mQuitAllowed){

thrownewIllegalStateException("Mainthreadnotallowedtoquit.");

}

synchronized(this){

if(mQuitting){

return;

}

mQuitting=true;

if(safe){

removeAllFutureMessagesLocked();//移除尚未处理的消息

}else{

removeAllMessagesLocked();//移除所有消息

}

//WecanassumemPtr!=0becausemQuittingwaspreviouslyfalse.

nativeWake(mPtr);

}

}

privatevoidremoveAllMessagesLocked(){

Messagep=mMessages;

while(p!=null){

Messagen=p.next;

p.recycleUnchecked();

p=n;

}

mMessages=null;

}

privatevoidremoveAllFutureMessagesLocked(){

finallongnow=SystemClock.uptimeMillis();

Messagep=mMessages;

if(p!=null){

if(p.when>now){

removeAllMessagesLocked();//移除尚未处理的消息

}else{//正在处理的消息不做处理

Messagen;

for(;;){

n=p.next;

if(n==null){

return;

}

if(n.when>now){

break;

}

p=n;

}

p.next=null;

do{

p=n;

n=p.next;

p.recycleUnchecked();

}while(n!=null);

}

}

}从上述代码中可以看出,当safe为true时,只移除尚未触发的所有消息,对于正在处理的消息不做处理,当safe为false时,移除所有消息。HandlerHandler是我们使用最多的类,主要用来发送消息和处理消息。先来看构造方法publicHandler(Callbackcallback,booleanasync){

if(FIND_POTENTIAL_LEAKS){

finalClass<?extendsHandler>klass=getClass();

if((klass.isAnonymousClass()||klass.isMemberClass()||klass.isLocalClass())&&

(klass.getModifiers()&Modifier.STATIC)==0){

Log.w(TAG,"ThefollowingHandlerclassshouldbestaticorleaksmightoccur:"+

klass.getCanonicalName());

}

}

//获取当前线程的Looper实例,如果不存在则抛出异常

mLooper=Looper.myLooper();

if(mLooper==null){

thrownewRuntimeException(

"Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()");

}

//关联Looper中的MessageQueue消息队列

mQueue=mLooper.mQueue;

mCallback=callback;

mAsynchronous=async;

}

publicHandler(Looperlooper,Callbackcallback,booleanasync){

mLooper=looper;

mQueue=looper.mQueue;

mCallback=callback;

mAsynchronous=async;

}构造方法主要有三个参数从源码中可看出,因为UI线程在启动时会自动创建Looper实例,所以一般我们在UI线程中使用Handler时不需要传递Looper对象。而在子线程中则必须手动调用Looper.prepare和Looper.loop方法,并传递给Handler,否则无法使用,这一点肯定有不少童鞋都遇到过。在拿到Looper对象后,Handler会获取Looper中的MessageQueue消息队列,这样就和MessageQueue关联上了。关联上MessageQueue,接下来那我们就看下Handler是如何发送消息的。Handler发送消息方法很多,实际上最后都是调用的enqueueMessage方法,看图说话主要看enqueueMessage方法publicbooleansendMessageAtTime(Messagemsg,longuptimeMillis){

MessageQueuequeue=mQueue;

if(queue==null){

RuntimeExceptione=newRuntimeException(

this+"sendMessageAtTime()calledwithnomQueue");

Log.w("Looper",e.getMessage(),e);

returnfalse;

}

returnenqueueMessage(queue,msg,uptimeMillis);

}

privatebooleanenqueueMessage(MessageQueuequeue,Messagemsg,longuptimeMillis){

msg.target=this;

if(mAsynchronous){

msg.setAsynchronous(true);

}

returnqueue.enqueueMessage(msg,uptimeMillis);

}可以看到在发送消息时给Message设置了target=this也就是当前的Handler对象,并调用了MessageQueue的enqueueMessage方法,这样就把消息存在消息队列,然后由Looper处理了。童鞋们应该记得之前在讲Looper时,说到Looper开启消息循环后,会不断从MessageQueue中取出Message,并调用msg.target.dispatchMessage(msg)来处理消息。接下来,就来看看Handler是如何接收消息的也就是dispatchMessage方法publicinterfaceCallba

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论