详解Android中Handler的实现原理
在Android中,只有主线程才能操作UI,但是主线程不能进行耗时操作,否则会阻塞线程,产生ANR异常,所以常常把耗时操作放到其它子线程进行。如果在子线程中需要更新UI,一般是通过Handler发送消息,主线程接受消息并且进行相应的逻辑处理。除了直接使用Handler,还可以通过View的post方法以及Activity的runOnUiThread方法来更新UI,它们内部也是利用了Handler。在上一篇文章AndroidAsyncTask源码分析中也讲到,其内部使用了Handler把任务的处理结果传回UI线程。
本文深入分析Android的消息处理机制,了解Handler的工作原理。
Handler
先通过一个例子看一下Handler的用法。
publicclassMainActivityextendsAppCompatActivity{ privatestaticfinalintMESSAGE_TEXT_VIEW=0; privateTextViewmTextView; privateHandlermHandler=newHandler(){ @Override publicvoidhandleMessage(Messagemsg){ switch(msg.what){ caseMESSAGE_TEXT_VIEW: mTextView.setText("UI成功更新"); default: super.handleMessage(msg); } } }; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbartoolbar=(Toolbar)findViewById(R.id.toolbar); setSupportActionBar(toolbar); mTextView=(TextView)findViewById(R.id.text_view); newThread(newRunnable(){ @Override publicvoidrun(){ try{ Thread.sleep(3000); }catch(InterruptedExceptione){ e.printStackTrace(); } mHandler.obtainMessage(MESSAGE_TEXT_VIEW).sendToTarget(); } }).start(); } }
上面的代码先是新建了一个Handler的实例,并且重写了handleMessage方法,在这个方法里,便是根据接受到的消息的类型进行相应的UI更新。那么看一下Handler的构造方法的源码:
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()); } } mLooper=Looper.myLooper(); if(mLooper==null){ thrownewRuntimeException( "Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()"); } mQueue=mLooper.mQueue; mCallback=callback; mAsynchronous=async; }
在构造方法中,通过调用Looper.myLooper()获得了Looper对象。如果mLooper为空,那么会抛出异常:"Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()",意思是:不能在未调用Looper.prepare()的线程创建handler。上面的例子并没有调用这个方法,但是却没有抛出异常。其实是因为主线程在启动的时候已经帮我们调用过了,所以可以直接创建Handler。如果是在其它子线程,直接创建Handler是会导致应用崩溃的。
在得到Handler之后,又获取了它的内部变量mQueue,这是MessageQueue对象,也就是消息队列,用于保存Handler发送的消息。
到此,Android消息机制的三个重要角色全部出现了,分别是Handler、Looper以及MessageQueue。一般在代码我们接触比较多的是Handler,但Looper与MessageQueue却是Handler运行时不可或缺的。
Looper
上一节分析了Handler的构造,其中调用了Looper.myLooper()方法,下面是它的源码:
staticfinalThreadLocal<Looper>sThreadLocal=newThreadLocal<Looper>();
publicstatic@NullableLoopermyLooper(){ returnsThreadLocal.get(); }
这个方法的代码很简单,就是从sThreadLocal中获取Looper对象。sThreadLocal是ThreadLocal对象,这说明Looper是线程独立的。
在Handler的构造中,从抛出的异常可知,每个线程想要获得Looper需要调用prepare()方法,继续看它的代码:
privatestaticvoidprepare(booleanquitAllowed){ if(sThreadLocal.get()!=null){ thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread"); } sThreadLocal.set(newLooper(quitAllowed)); }
同样很简单,就是给sThreadLocal设置一个Looper。不过需要注意的是如果sThreadLocal已经设置过了,那么会抛出异常,也就是说一个线程只会有一个Looper。创建Looper的时候,内部会创建一个消息队列:
privateLooper(booleanquitAllowed){ mQueue=newMessageQueue(quitAllowed); mThread=Thread.currentThread(); }
现在的问题是,Looper看上去很重要的样子,它到底是干嘛的?
回答:Looper开启消息循环系统,不断从消息队列MessageQueue取出消息交由Handler处理。
为什么这样说呢,看一下Looper的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 Printerlogging=me.mLogging; if(logging!=null){ logging.println(">>>>>Dispatchingto"+msg.target+""+ msg.callback+":"+msg.what); } msg.target.dispatchMessage(msg); 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(); } }
这个方法的代码有点长,不去追究细节,只看整体逻辑。可以看出,在这个方法内部有个死循环,里面通过MessageQueue的next()方法获取下一条消息,没有获取到会阻塞。如果成功获取新消息,便调用msg.target.dispatchMessage(msg),msg.target是Handler对象(下一节会看到),dispatchMessage则是分发消息(此时已经运行在UI线程),下面分析消息的发送及处理流程。
消息发送与处理
在子线程发送消息时,是调用一系列的sendMessage、sendMessageDelayed以及sendMessageAtTime等方法,最终会辗转调用sendMessageAtTime(Messagemsg,longuptimeMillis),代码如下:
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); }
这个方法就是调用enqueueMessage在消息队列中插入一条消息,在enqueueMessage总中,会把msg.target设置为当前的Handler对象。
消息插入消息队列后,Looper负责从队列中取出,然后调用Handler的dispatchMessage方法。接下来看看这个方法是怎么处理消息的:
publicvoiddispatchMessage(Messagemsg){ if(msg.callback!=null){ handleCallback(msg); }else{ if(mCallback!=null){ if(mCallback.handleMessage(msg)){ return; } } handleMessage(msg); } }
首先,如果消息的callback不是空,便调用handleCallback处理。否则判断Handler的mCallback是否为空,不为空则调用它的handleMessage方法。如果仍然为空,才调用Handler自身的handleMessage,也就是我们创建Handler时重写的方法。
如果发送消息时调用Handler的post(Runnabler)方法,会把Runnable封装到消息对象的callback,然后调用sendMessageDelayed,相关代码如下:
publicfinalbooleanpost(Runnabler) { returnsendMessageDelayed(getPostMessage(r),0); } privatestaticMessagegetPostMessage(Runnabler){ Messagem=Message.obtain(); m.callback=r; returnm; }
此时在dispatchMessage中便会调用handleCallback进行处理:
privatestaticvoidhandleCallback(Messagemessage){ message.callback.run(); }
可以看到是直接调用了run方法处理消息。
如果在创建Handler时,直接提供一个Callback对象,消息就交给这个对象的handleMessage方法处理。Callback是Handler内部的一个接口:
publicinterfaceCallback{ publicbooleanhandleMessage(Messagemsg); }
以上便是消息发送与处理的流程,发送时是在子线程,但处理时dispatchMessage方法运行在主线程。
总结
至此,Android消息处理机制的原理就分析结束了。现在可以知道,消息处理是通过Handler、Looper以及MessageQueue共同完成。Handler负责发送以及处理消息,Looper创建消息队列并不断从队列中取出消息交给Handler,MessageQueue则用于保存消息。