Android中Thread、Handler、Looper、MessageQueue的原理分析
在Android开发当中,Thread、Handler、Looper这几个类是特别常见,在刚开始学习Android的时候对这些类可能并不是很清晰。下面我们就一起从源码的角度剖析一下这几个类的工作原理。
Thread
首先是Thread, 我们都知道一个Thread就是一个线程对象,只要在run方法中填写自己的代码然后启动该线程就可以实现多线程操作。例如 :
[java] view plain copy- newThread(){
- publicvoidrun(){
- //耗时的操作
- };
- }.start();
我们知道,针对上面的代码中,当执行完run中的操作时,整个线程就会结束,并不会一直执行下去。而我们的应用程序会一直执行,除非你退出或者应用程序抛出异常。这又引入了另外一个概念,即消息队列。在Android应用启动时,会默认有一个主线程(UI线程),在这个线程中会关联一个消息队列,所有的操作都会被封装成消息然后交给主线程来处理。为了保证主线程不会主动退出,会将取消息的操作放在一个死循环中,这样程序就相当于一直在执行死循环,因此不会退出。
示例图如下 :
Android应用程序的入口为ActivityThread.main方法,详情请参考Android应用程序进程启动过程的源代码分析,UI线程的消息循环就是在这个方法中创建的,源码如下:
[java] view plain copy- publicstaticvoidmain(String[]args){
- SamplingProfilerIntegration.start();
- CloseGuard.setEnabled(false);
- Environment.initForCurrentUser();
- //Setthereporterforeventlogginginlibcore
- EventLogger.setReporter(newEventLoggingReporter());
- Process.setArgV0("<pre-initialized>");
- Looper.prepareMainLooper();//1、创建消息循环Looper
- ActivityThreadthread=newActivityThread();
- thread.attach(false);
- if(sMainThreadHandler==null){
- sMainThreadHandler=thread.getHandler();//UI线程的Handler
- }
- AsyncTask.init();
- if(false){
- Looper.myLooper().setMessageLogging(new
- LogPrinter(Log.DEBUG,"ActivityThread"));
- }
- Looper.loop();//2、执行消息循环
- thrownewRuntimeException("Mainthreadloopunexpectedlyexited");
- }
执行ActivityThread.main方法后,应用程序就启动了,并且会一直从消息队列中取消息,然后处理消息。那么系统是如何将消息投递到消息队列中的?又是如何从消息队列中获取消息并且处理消息的呢? 答案就是Handler。
Handler
在我们在子线程中执行完耗时操作后很多情况下我们需要更新UI,但我们都知道,不能在子线程中更新UI。此时最常用的手段就是通过Handler将一个消息post到UI线程中,然后再在Handler的handleMessage方法中进行处理。但是有一个点要注意,那就是该Handler必须在主线程中创建!!简单示例如下:
[java] view plain copy- classMyHandlerextendsHandler{
- @Override
- publicvoidhandleMessage(Messagemsg){
- //更新UI
- }
- }
- MyHandlermHandler=newMyHandler();
- //开启新的线程
- newThread(){
- publicvoidrun(){
- //耗时操作
- mHandler.sendEmptyMessage(123);
- };
- }.start();
为什么必须要这么做呢?其实每个Handler都会关联一个消息队列,消息队列被封装在Lopper中,而每个Looper又会关联一个线程(ThreadLocal),也就是每个消息队列会关联一个线程。Handler就是一个消息处理器,将消息投递给消息队列,然后再由对应的线程从消息队列中挨个取出消息,并且执行。默认情况下,消息队列只有一个,即主线程的消息队列,这个消息队列是在ActivityThread.main方法中创建的,通过Lopper.prepareMainLooper()来创建,然后最后执行Looper.loop()来启动消息循环。那么Handler是如何关联消息队列以及线程的呢?我们看看如下源码 :
[java] view plain copy- publicHandler(){
- 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();//获取Looper
- if(mLooper==null){
- thrownewRuntimeException(
- "Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()");
- }
- mQueue=mLooper.mQueue;//获取消息队列
- mCallback=null;
- }
从Handler默认的构造函数中我们可以看到,Handler会在内部通过Looper.getLooper()来获取Looper对象,并且与之关联,最重要的就是消息队列。那么Looper.getLooper()又是如何工作的呢?我们继续往下看.
[java] view plain copy- /**
- *ReturntheLooperobjectassociatedwiththecurrentthread.Returns
- *nullifthecallingthreadisnotassociatedwithaLooper.
- */
- publicstaticLoopermyLooper(){
- returnsThreadLocal.get();
- }
- /**
- *Initializethecurrentthreadasalooper,markingitasan
- *application'smainlooper.Themainlooperforyourapplication
- *iscreatedbytheAndroidenvironment,soyoushouldneverneed
- *tocallthisfunctionyourself.Seealso:{@link#prepare()}
- */
- publicstaticvoidprepareMainLooper(){
- prepare();
- setMainLooper(myLooper());
- myLooper().mQueue.mQuitAllowed=false;
- }
- privatesynchronizedstaticvoidsetMainLooper(Looperlooper){
- mMainLooper=looper;
- }
- /**Initializethecurrentthreadasalooper.
- *Thisgivesyouachancetocreatehandlersthatthenreference
- *thislooper,beforeactuallystartingtheloop.Besuretocall
- *{@link#loop()}aftercallingthismethod,andenditbycalling
- *{@link#quit()}.
- */
- publicstaticvoidprepare(){
- if(sThreadLocal.get()!=null){
- thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");
- }
- sThreadLocal.set(newLooper());
- }
我们看到myLooper()方法是通过sThreadLocal.get()来获取的,关于ThreadLocal的资料请参考ThreadLocal多线程实例详解。那么Looper对象又是什么时候存储在sThreadLocal中的呢? 眼尖的朋友可能看到了,上面贴出的代码中给出了一个熟悉的方法,prepareMainLooper(),在这个方法中调用了prepare()方法,在这个方法中创建了一个Looper对象,并且将该对象设置给了sThreadLocal。这样,队列就与线程关联上了!!!不同的线程是不能访问对方的消息队列的。再回到Handler中来,消息队列通过Looper与线程关联上,而Handler又与Looper关联,因此Handler最终就和线程、线程的消息队列关联上了。这就能解释上面提到的问题了,“为什么要更新UI的Handler必须要在主线程中创建?”。就是因为Handler要与主线程的消息队列关联上,这样handleMessage才会执行在UI线程,此时更新UI才是线程安全的!!!
Looper与MessageQueue
创建了Looper后,如何执行消息循环呢?通过Handler来post消息给消息队列( 链表 ),那么消息是如何被处理的呢?答案就是在消息循环中,消息循环的建立就是通过Looper.loop()方法。源码如下 :
[java] view plain copy- /**
- *Runthemessagequeueinthisthread.Besuretocall
- *{@link#quit()}toendtheloop.
- */
- publicstaticvoidloop(){
- Looperme=myLooper();
- if(me==null){
- thrownewRuntimeException("NoLooper;Looper.prepare()wasn'tcalledonthisthread.");
- }
- MessageQueuequeue=me.mQueue;//1、获取消息队列
- //代码省略
- while(true){//2、死循环,即消息循环
- Messagemsg=queue.next();//3、获取消息(mightblock)
- if(msg!=null){
- if(msg.target==null){
- //Notargetisamagicidentifierforthequitmessage.
- return;
- }
- longwallStart=0;
- longthreadStart=0;
- //Thismustbeinalocalvariable,incaseaUIeventsetsthelogger
- Printerlogging=me.mLogging;
- if(logging!=null){
- logging.println(">>>>>Dispatchingto"+msg.target+""+
- msg.callback+":"+msg.what);
- wallStart=SystemClock.currentTimeMicro();
- threadStart=SystemClock.currentThreadTimeMicro();
- }
- msg.target.dispatchMessage(msg);//4、处理消息
- //代码省略
- msg.recycle();
- }
- }
- }
可以看到,loop方法中实质上就是建立一个死循环,然后通过从消息队列中挨个取出消息,最后处理消息的过程。对于Looper我们总结一下 : 通过Looper.prepare()来创建Looper对象(消息队列封装在Looper对象中),并且保存在sThreadLoal中,然后通过Looper.loop()来执行消息循环,这两步通常是成对出现的!!
最后我们看看消息处理机制,我们看到代码中第4步通过msg.target.dispatchMessage(msg)来处理消息。其中msg是Message类型,我们看源码 :
- publicfinalclassMessageimplementsParcelable{
- publicintwhat;
- publicintarg1;
- publicintarg2;
- publicObjectobj;
- intflags;
- longwhen;
- Bundledata;
- Handlertarget;//target处理
- Runnablecallback;//Runnable类型的callback
- //sometimeswestorelinkedlistsofthesethings
- Messagenext;//下一条消息,消息队列是链式存储的
- //代码省略....
- }
从源码中可以看到,target是Handler类型。实际上就是转了一圈,通过Handler将消息投递给消息队列,消息队列又将消息分发给Handler来处理。我们继续看
- /**
- *Subclassesmustimplementthistoreceivemessages.
- */
- publicvoidhandleMessage(Messagemsg){
- }
- privatefinalvoidhandleCallback(Messagemessage){
- message.callback.run();
- }
- /**
- *Handlesystemmessageshere.
- */
- publicvoiddispatchMessage(Messagemsg){
- if(msg.callback!=null){
- handleCallback(msg);
- }else{
- if(mCallback!=null){
- if(mCallback.handleMessage(msg)){
- return;
- }
- }
- handleMessage(msg);
- }
- }
可以看到,dispatchMessage只是一个分发的方法,如果Runnable类型的callback为空则执行handlerMessage来处理消息,该方法为空,我们会将更新UI的代码写在该函数中;如果callback不为空,则执行handleCallback来处理,该方法会调用callback的run方法。其实这是Handler分发的两种类型,比如我们post(Runnable callback)则callback就不为空,当我们使用Handler来sendMessage时通常不会设置callback,因此也就执行handlerMessage这个分支。我们看看两种实现 :
[java] view plain copy- publicfinalbooleanpost(Runnabler)
- {
- returnsendMessageDelayed(getPostMessage(r),0);
- }
- privatefinalMessagegetPostMessage(Runnabler){
- Messagem=Message.obtain();
- m.callback=r;
- returnm;
- }
- publicfinalbooleansendMessageDelayed(Messagemsg,longdelayMillis)
- {
- if(delayMillis<0){
- delayMillis=0;
- }
- returnsendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);
- }
- publicbooleansendMessageAtTime(Messagemsg,longuptimeMillis)
- {
- booleansent=false;
- MessageQueuequeue=mQueue;
- if(queue!=null){
- msg.target=this;//设置消息的target为当前Handler对象
- sent=queue.enqueueMessage(msg,uptimeMillis);//将消息插入到消息队列
- }
- else{
- RuntimeExceptione=newRuntimeException(
- this+"sendMessageAtTime()calledwithnomQueue");
- Log.w("Looper",e.getMessage(),e);
- }
- returnsent;
- }
可以看到,在post(Runnable r)时,会将Runnable包装成Message对象,并且将Runnable对象设置给Message对象的callback字段,最后会将该Message对象插入消息队列。sendMessage也是类似实现 :
- publicfinalbooleansendMessage(Messagemsg)
- {
- returnsendMessageDelayed(msg,0);
- }
不管是post一个Runnbale还是Message,都会调用sendMessageDelayed(msg, time)方法。
子线程中创建Handler为何会抛出异常 ?
我们看如下代码 : [java] view plain copy- newThread(){
- Handlerhandler=null;
- publicvoidrun(){
- handler=newHandler();
- };
- }.start();
- /**
- *Defaultconstructorassociatesthishandlerwiththequeueforthe
- *currentthread.
- *
- *Ifthereisn'tone,thishandlerwon'tbeabletoreceivemessages.
- */
- publicHandler(){
- //代码省略
- mLooper=Looper.myLooper();//获取myLooper
- if(mLooper==null){
- thrownewRuntimeException(
- "Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()");//抛出异常
- }
- mQueue=mLooper.mQueue;
- mCallback=null;
- }
我们可以看到,当mLooper对象为空时,抛出了该异常。这是因为该线程中的Looper对象还没有创建,因此sThreadLocal.get()会返回null。解决方法如下 : [java] view plain copy
- newThread(){
- Handlerhandler=null;
- publicvoidrun(){
- Looper.prepare();//1、创建Looper,并且会绑定到ThreadLocal中
- handler=newHandler();
- Looper.loop();//2、启动消息循环
- };
- }.start();
总结
在应用启动时,会开启一个主线程(UI线程),并且启动消息循环,应用不停地从该消息队列中取出、处理消息达到程序运行的效果。Looper对象封装了消息队列,Looper对象是ThreadLocal的,不同线程之间的Looper对象不能共享与访问。而Handler通过与Looper对象绑定来实现与执行线程的绑定,handler会把Runnable(包装成Message)或者Message对象追加到与线程关联的消息队列中,然后在消息循环中挨个取出消息,并且处理消息。当Handler绑定的Looper是主线程的Looper,则该Handler可以在handleMessage中更新UI,否则更新UI则会抛出异常! 其实我们可以把Handler、Looper、Thread想象成一个生产线,工人(搬运工)相当于Handler,负责将货物搬到传输带上(Handler将消息传递给消息队列);传送带扮演消息队列的角色,负责传递货物,货物会被挨取出,并且输送到目的地 ( target来处理 );而货物到达某个车间后再被工人处理,车间就扮演了Thread这个角色,每个车间有自己独立的传送带,车间A的货物不能被车间B的拿到,即相当于ThreadLocal( 车间独有 )。 转自:http://blog.csdn.net/bboyfeiyu/article/details/38555547更多相关文章
- 【转】Android(安卓)用户事件输入路径(Message/Event, Dispatch
- Android(安卓)进程间通信的方式
- 关于Android多进程
- 安卓 每日一题 2020年5月、6月
- android AsyncTask使用总结
- Android中AsyncTask(异步执行任务)的使用
- 图解 Android(安卓)事件分发机制 和 handler 机制
- Android之Handler有感(一)
- Android的消息处理机制—Looper,Handler,Message