上一篇博文给出了Android中基于Handler Looper机制实现线程间通信的两个典型实例。本文将对该机制的基本原理进行较深入的研究。个人认为,学好Android编程最好的老师就是Android的源代码,下面将基于Android-19的源码进行分析,重点阐述分析思路。

要分析Handler Looper机制,自然想到去看Handler类和Looper类的源码(分别位于Handler.java和Looper.java两个文件中)。简单阅读两个类的描述后,在Looper类的描述中能找到以下一段示例代码。

*<p>ThisisatypicalexampleoftheimplementationofaLooperthread,*usingtheseparationof{@link#prepare}and{@link#loop}tocreatean*initialHandlertocommunicatewiththeLooper.**<pre>*classLooperThreadextendsThread{*publicHandlermHandler;**publicvoidrun(){*Looper.prepare();**mHandler=newHandler(){*publicvoidhandleMessage(Messagemsg){*//processincomingmessageshere*}*};**Looper.loop();*}*}</pre>*/

这段代码给出了Handler Looper机制实现进程间通信的三大基本步骤,包括Looper的两个函数prepare()和loop(),以及Handler的handleMessage函数。上一篇博文中实例二模拟子线程向UI主线程传递信息的程序就基本上是直接copy这段示例代码实现的。

先看第一个步骤:调用Looper.prepare()函数,猜测应该是创建Looper对象,做些初始化工作。代码如下:

/**Initializethecurrentthreadasalooper.*Thisgivesyouachancetocreatehandlersthatthenreference*thislooper,beforeactuallystartingtheloop.Besuretocall*{@link#loop()}aftercallingthismethod,andenditbycalling*{@link#quit()}.*/publicstaticvoidprepare(){prepare(true);}

直接调用了重载函数prepare(true);

//ThreadLocal实例为多个线程共享,但保证每个线程的存储空间相互独立staticfinalThreadLocal<Looper>sThreadLocal=newThreadLocal<Looper>();//消息队列finalMessageQueuemQueue;//当前线程引用finalThreadmThread;privatestaticvoidprepare(booleanquitAllowed){//保证一个线程最多只能创建一个Looper对象。if(sThreadLocal.get()!=null){thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");}//创建Looper对象并存储到当前线程独立的存储空间中sThreadLocal.set(newLooper(quitAllowed));}privateLooper(booleanquitAllowed){//创建消息队列(线程间即通过该消息队列实现通信)mQueue=newMessageQueue(quitAllowed);//获得当前线程的引用mThread=Thread.currentThread();}

看到这里明白了,Looper的prepare函数实际上创建了Looper对象并把对象保存到当前线程独立的存储空间中。这里,Looper的构造函数是私有的,所以外部无法直接通过new Looper()随意创建Looper对象。而只能通过Looper的prepare()函数创建。这样做能够保证对于某一个线程,最多只会创建一个Looper对象的实例,这实际上就是设计模拟中的单体模式。此外,在Looper的私有构造函数中还创建了消息队列并获得当前线程(即创建Looper的线程)的引用。

先跳过Handler.handleMessage(Message msg),直接看Looper.loop()的实现。

/***Runthemessagequeueinthisthread.Besuretocall*{@link#quit()}toendtheloop.*/publicstaticvoidloop(){ui//myLooper()函数通过sThreadLocal.get()判断当前线程是否已经创建了Looper的实例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();//mightblockif(msg==null){//Nomessageindicatesthatthemessagequeueisquitting.return;}//Thismustbeinalocalvariable,incaseaUIeventsetstheloggerPrinterlogging=me.mLogging;if(logging!=null){logging.println(">>>>>Dispatchingto"+msg.target+""+msg.callback+":"+msg.what);}//这里是关键,调用了dispatchMessage函数对从消息队列取出的msg进行分派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.recycle();}}

到这里,Looper扮演的角色已经明朗了,主要就是通过loop()函数中的那个无限循环不断从消息队列中取出消息,并通过dispatchMessage()方法将消息派送出去。那么消息队列中的消息从哪里来,又会被派送到哪里呢?

先来分析第一个问题,消息从哪里来。上一篇博文的实例中,消息源线程均通过调用Handler的sendMessage()函数来发送消息。进入Handler.java文件看其中的sendMessage()函数。

publicfinalbooleansendMessage(Messagemsg){returnsendMessageDelayed(msg,0);}

这里调用了sendMessageDelayed,延时0毫秒。

publicfinalbooleansendMessageDelayed(Messagemsg,longdelayMillis){if(delayMillis<0){delayMillis=0;}returnsendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);}

进一步调用了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);}

再看enqueueMessage函数。

privatebooleanenqueueMessage(MessageQueuequeue,Messagemsg,longuptimeMillis){//设置targethandlermsg.target=this;if(mAsynchronous){msg.setAsynchronous(true);}//将消息插入到消息队列中returnqueue.enqueueMessage(msg,uptimeMillis);}

看到这里已经明朗了,消息源线程通过Handler.sendMessage发送消息,实际上就是把消息插入了与之关联的消息队列中。在enqueueMessage函数中有一条关键语句msg.target = this,通过这条语句就把Handler和Looper关联起来了(在Looper.loop()的循环中就是通过msg.target属性找到发送消息的Handler并调用其dispatchMessage()函数派发消息的).

搞清楚了消息从哪里来,接下来分析消息被派发到哪里,接着看dispatchMessage()函数的实现。

/***Handlesystemmessageshere.*/publicvoiddispatchMessage(Messagemsg){//若消息对象实现了其中的Runnable接口,调用对应的回调函数,即为message.callback.run())if(msg.callback!=null){handleCallback(msg);}else{//若实现了Handler类的Callback接口,调用接口的回调函数if(mCallback!=null){if(mCallback.handleMessage(msg)){return;}}//将消息返回Handler的消息处理回调函数(是Handler的成员函数,示例代码中回调的就是该函数)handleMessage(msg);}}

可见,dispatchMessage函数中根据msg,handler对象的设置情况调用相应的消息处理回调函数,我们只需要在这个回调函数中添加代码,就可以进行消息处理。示例代码的第二个步骤中的handleMessage函数就是在这里被回调的。

下面回到示例代码中的第二个步骤:

*mHandler=newHandler(){*publicvoidhandleMessage(Messagemsg){*//processincomingmessageshere*}*};

这段代码创建了Handler的对象,并覆盖了其中的HandleMessage方法,用户可以添加自己的消息处理函数。handleMessage回调函数上面已经分析过了,下面主要看看创建handler对象时都做了哪些事情。转入Handler.java文件,看Handler的构造函数(找不带参数那个)。

/***Defaultconstructorassociatesthishandlerwiththe{@linkLooper}forthe*currentthread.**Ifthisthreaddoesnothavealooper,thishandlerwon'tbeabletoreceivemessages*soanexceptionisthrown.*/publicHandler(){this(null,false);}

调用了下面的双参数的构造函数。

finalLoopermLooper;finalMessageQueuemQueue;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关联的消息队列mQueue=mLooper.mQueue;mCallback=callback;mAsynchronous=async;}

到这里发现Handler的构造函数主要做了两件事:1)获得当前线程Looper对象的应用.2)获得与Looper关联的消息队列的引用。到这里,Handler、Looper、消息队列三者就已经关联起来了。

下面通过一个示意图对上面的分析进行总结。

由图可见,基于Handler Looper机制传递消息主要包括以下几个步骤。

(1)目标线程调用Looper.prepare()创建Looper对象和消息队列。

(2)目标线程通过new Handler()创建handler对象,将Handler,Looper,消息队列三者关联起来。并覆盖其handleMessage函数。

(3)目标线程调用Looper.loop()监听消息队列。

(4)消息源线程调用Handler.sendMessage发送消息。

(5)消息源线程调用MessageQueue.enqueueMessage将待发消息插入消息队列。

(6)目标线程的loop()检测到消息队列有消息插入,将其取出。

(7)目标线程将取出的消息通过Handler.dispatchMessage派发给Handler.handleMessage进行消息处理。

到这里整个Android的Handler Looper机制传递消息原理就分析完毕了。还有一个问题值得一提,回顾一下上一篇博文的示例1模拟从网络上下载数据的程序,UI主线程只通过new Handler()创建了Handler对象的实例并覆盖了其handleMessage函数。在代码中并没有看到调用Looper.prepare和Looper.loop(),那么UI主线程中没有创建Looper对象吗?下面就来分析这个问题,既然是UI主线程,那么自然是在启动应用时候由系统自动创建的,创建过程中是否已经创建了Looper对象并调用loop()进行监听了呢?转到ActivityThread.java,找到其中的main函数,这里即为Android程序的入口。在其中能看到以下两行代码。

Looper.prepareMainLooper();............Looper.loop();

再看prepareMainLooper函数的实现:

publicstaticvoidprepareMainLooper(){prepare(false);synchronized(Looper.class){if(sMainLooper!=null){thrownewIllegalStateException("ThemainLooperhasalreadybeenprepared.");}sMainLooper=myLooper();}}

在这里调用了prepare创建Looper对象。所以说,对于UI主线程而言,其Looper对象是由系统创建好的,用户就无需自行创建了。


更多相关文章

  1. 箭头函数的基础使用
  2. Python技巧匿名函数、回调函数和高阶函数
  3. 浅析android通过jni控制service服务程序的简易流程
  4. 浅析Android中的消息机制-解决:Only the original thread that cr
  5. Android异步消息机制之Handler
  6. Android的Handler机制详解3_Looper.looper()不会卡死主线程
  7. Android(安卓)bluetooth介绍(四): a2dp connect流程分析
  8. Android之Handler用法总结
  9. Android架构分析之使用自定义硬件抽象层(HAL)模块

随机推荐

  1. Android之使用Android-query框架进行开发
  2. Android中WebView拦截替换网络请求数据
  3. ANDROID物联网开发
  4. Android开发资源完全汇总
  5. Android(安卓)APK包文件解析
  6. 跨平台应用app开发实践方案 Qt5 for Andr
  7. Android开发:最全面、最易懂的Android屏幕
  8. 【android】android 开发错误点滴积累5月
  9. Android开发资源完全汇总
  10. android 7种网络连接方式--IT蓝豹