这篇文章是在一个论坛里看到的,作者写得相当不错,对于Android的Handler消息机制分析得也相当透彻.下面我们来看一下:

Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易、更好地架构系统,避免一些低级的错误。在学习Android中消息机制之前,我们先了解与消息有关的几个类:

1.Message

消息对象,顾名思义就是记录消息信息的类。这个类有几个比较重要的字段:

a.arg1arg2:我们可以使用两个字段用来存放我们需要传递的整型值,在Service中,我们可以用来存放ServiceID

b.obj:该字段是Object类型,我们可以让该字段传递某个多项到消息的接受者中。

c.what:这个字段可以说是消息的标志,在消息处理中,我们可以根据这个字段的不同的值进行不同的处理,类似于我们在处理Button事件时,通过switch(v.getId())判断是点击了哪个按钮。

在使用Message时,我们可以通过new Message()创建一个Message实例,但是Android更推荐我们通过Message.obtain()或者Handler.obtainMessage()获取Message对象。这并不一定是直接创建一个新的实例,而是先从消息池中看有没有可用的Message实例,存在则直接取出并返回这个实例。反之如果消息池中没有可用的Message实例,则根据给定的参数new一个新Message对象。通过分析源码可得知,Android系统默认情况下在消息池中实例化10Message对象。

2.MessageQueue

消息队列,用来存放Message对象的数据结构,按照“先进先出”的原则存放消息。存放并非实际意义的保存,而是将Message对象以链表的方式串联起来的。MessageQueue对象不需要我们自己创建,而是有Looper对象对其进行管理,一个线程最多只可以拥有一个MessageQueue。我们可以通过Looper.myQueue()获取当前线程中的MessageQueue

3.Looper

MessageQueue的管理者,在一个线程中,如果存在Looper对象,则必定存在MessageQueue对象,并且只存在一个Looper对象和一个MessageQueue对象。在Android系统中,除了主线程有默认的Looper对象,其它线程默认是没有Looper对象。如果想让我们新创建的线程拥有Looper对象时,我们首先应调用Looper.prepare()方法,然后再调用Looper.loop()方法。典型的用法如下:

view plain
  1. classLooperThreadextendsThread
  2. {
  3. publicHandlermHandler;
  4. publicvoidrun()
  5. {
  6. Looper.prepare();
  7. //其它需要处理的操作
  8. Looper.loop();
  9. }
  10. }

倘若我们的线程中存在Looper对象,则我们可以通过Looper.myLooper()获取,此外我们还可以通过Looper.getMainLooper()获取当前应用系统中主线程Looper对象。在这个地方有一点需要注意,假如Looper对象位于应用程序主线程中,则Looper.myLooper()Looper.getMainLooper()获取的是同一个对象。

4.Handler

消息的处理者。通过Handler对象我们可以封装Message对象,然后通过sendMessage(msg)Message对象添加到MessageQueue中;当MessageQueue循环到该Message时,就会调用该Message对象对应的handler对象的handleMessage()方法对其进行处理。由于是在handleMessage()方法中处理消息,因此我们应该编写一个类继承自Handler,然后在handleMessage()处理我们需要的操作。

下面我们通过跟踪代码分析在Android中是如何处理消息。首先贴上测试代码:

view plain
  1. /**
  2. *
  3. *@authorcoolszy
  4. *@bloghttp://blog.csdn.net/coolszy
  5. *
  6. */
  7. publicclassMessageServiceextendsService
  8. {
  9. privatestaticfinalStringTAG="MessageService";
  10. privatestaticfinalintKUKA=0;
  11. privateLooperlooper;
  12. privateServiceHandlerhandler;
  13. /**
  14. *由于处理消息是在Handler的handleMessage()方法中,因此我们需要自己编写类
  15. *继承自Handler类,然后在handleMessage()中编写我们所需要的功能代码
  16. *@authorcoolszy
  17. *
  18. */
  19. privatefinalclassServiceHandlerextendsHandler
  20. {
  21. publicServiceHandler(Looperlooper)
  22. {
  23. super(looper);
  24. }
  25. @Override
  26. publicvoidhandleMessage(Messagemsg)
  27. {
  28. //根据what字段判断是哪个消息
  29. switch(msg.what)
  30. {
  31. caseKUKA:
  32. //获取msg的obj字段。我们可在此编写我们所需要的功能代码
  33. Log.i(TAG,"Theobjfieldofmsg:"+msg.obj);
  34. break;
  35. //othercases
  36. default:
  37. break;
  38. }
  39. //如果我们Service已完成任务,则停止Service
  40. stopSelf(msg.arg1);
  41. }
  42. }
  43. @Override
  44. publicvoidonCreate()
  45. {
  46. Log.i(TAG,"MessageService-->onCreate()");
  47. //默认情况下Service是运行在主线程中,而服务一般又十分耗费时间,如果
  48. //放在主线程中,将会影响程序与用户的交互,因此把Service
  49. //放在一个单独的线程中执行
  50. HandlerThreadthread=newHandlerThread("MessageDemoThread",Process.THREAD_PRIORITY_BACKGROUND);
  51. thread.start();
  52. //获取当前线程中的looper对象
  53. looper=thread.getLooper();
  54. //创建Handler对象,把looper传递过来使得handler、
  55. //looper和messageQueue三者建立联系
  56. handler=newServiceHandler(looper);
  57. }
  58. @Override
  59. publicintonStartCommand(Intentintent,intflags,intstartId)
  60. {
  61. Log.i(TAG,"MessageService-->onStartCommand()");
  62. //从消息池中获取一个Message实例
  63. Messagemsg=handler.obtainMessage();
  64. //arg1保存线程的ID,在handleMessage()方法中
  65. //我们可以通过stopSelf(startId)方法,停止服务
  66. msg.arg1=startId;
  67. //msg的标志
  68. msg.what=KUKA;
  69. //在这里我创建一个date对象,赋值给obj字段
  70. //在实际中我们可以通过obj传递我们需要处理的对象
  71. Datedate=newDate();
  72. msg.obj=date;
  73. //把msg添加到MessageQueue中
  74. handler.sendMessage(msg);
  75. returnSTART_STICKY;
  76. }
  77. @Override
  78. publicvoidonDestroy()
  79. {
  80. Log.i(TAG,"MessageService-->onDestroy()");
  81. }
  82. @Override
  83. publicIBinderonBind(Intentintent)
  84. {
  85. returnnull;
  86. }
  87. }

运行结果:


注:在测试代码中我们使用了HandlerThread类,该类是Thread的子类,该类运行时将会创建looper对象,使用该类省去了我们自己编写Thread子类并且创建Looper的麻烦。

下面我们分析下程序的运行过程:

1.onCreate()

首先启动服务时将会调用onCreate()方法,在该方法中我们new了一个HandlerThread对象,提供了线程的名字和优先级。

紧接着我们调用了start()方法,执行该方法将会调用HandlerThread对象的run()方法:

view plain
  1. publicvoidrun(){
  2. mTid=Process.myTid();
  3. Looper.prepare();
  4. synchronized(this){
  5. mLooper=Looper.myLooper();
  6. notifyAll();
  7. }
  8. Process.setThreadPriority(mPriority);
  9. onLooperPrepared();
  10. Looper.loop();
  11. mTid=-1;
  12. }

run()方法中,系统给线程添加的Looper,同时调用了Looperloop()方法:

view plain
  1. publicstaticfinalvoidloop(){
  2. Looperme=myLooper();
  3. MessageQueuequeue=me.mQueue;
  4. while(true){
  5. Messagemsg=queue.next();//mightblock
  6. //if(!me.mRun){
  7. //break;
  8. //}
  9. if(msg!=null){
  10. if(msg.target==null){
  11. //Notargetisamagicidentifierforthequitmessage.
  12. return;
  13. }
  14. if(me.mLogging!=null)me.mLogging.println(
  15. ">>>>>Dispatchingto"+msg.target+""
  16. +msg.callback+":"+msg.what
  17. );
  18. msg.target.dispatchMessage(msg);
  19. if(me.mLogging!=null)me.mLogging.println(
  20. "<<<<<Finishedto"+msg.target+""
  21. +msg.callback);
  22. msg.recycle();
  23. }
  24. }
  25. }

通过源码我们可以看到loop()方法是个死循环,将会不停的从MessageQueue对象中获取Message对象,如果MessageQueue对象中不存在Message对象,则结束本次循环,然后继续循环;如果存在Message对象,则执行msg.target.dispatchMessage(msg),但是这个msg.target字段的值是什么呢?我们先暂时停止跟踪源码,返回到onCreate()方法中。线程执行完start()方法后,我们可以获取线程的Looper对象,然后new一个ServiceHandler对象,我们把Looper对象传到ServiceHandler构造函数中将使handlerloopermessageQueue三者建立联系。

2.onStartCommand()

执行完onStart()方法后,将执行onStartCommand()方法。首先我们从消息池中获取一个Message实例,然后给Message对象的arg1whatobj三个字段赋值。紧接着调用sendMessage(msg)方法,我们跟踪源代码,该方法将会调用sendMessageDelayed(msg, 0)方法,而sendMessageDelayed()方法又会调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在该方法中我们要注意该句代码msg.target = thismsgtarget指向了this,而this就是ServiceHandler对象,因此msgtarget字段指向了ServiceHandler对象,同时该方法又调用MessageQueueenqueueMessage(msg, uptimeMillis)方法:

view plain
  1. finalbooleanenqueueMessage(Messagemsg,longwhen){
  2. if(msg.when!=0){
  3. thrownewAndroidRuntimeException(msg
  4. +"Thismessageisalreadyinuse.");
  5. }
  6. if(msg.target==null&&!mQuitAllowed){
  7. thrownewRuntimeException("Mainthreadnotallowedtoquit");
  8. }
  9. synchronized(this){
  10. if(mQuiting){
  11. RuntimeExceptione=newRuntimeException(
  12. msg.target+"sendingmessagetoaHandleronadeadthread");
  13. Log.w("MessageQueue",e.getMessage(),e);
  14. returnfalse;
  15. }elseif(msg.target==null){
  16. mQuiting=true;
  17. }
  18. msg.when=when;
  19. //Log.d("MessageQueue","Enqueing:"+msg);
  20. Messagep=mMessages;
  21. if(p==null||when==0||when<p.when){
  22. msg.next=p;
  23. mMessages=msg;
  24. this.notify();
  25. }else{
  26. Messageprev=null;
  27. while(p!=null&&p.when<=when){
  28. prev=p;
  29. p=p.next;
  30. }
  31. msg.next=prev.next;
  32. prev.next=msg;
  33. this.notify();
  34. }
  35. }
  36. returntrue;
  37. }

该方法主要的任务就是把Message对象的添加到MessageQueue中(数据结构最基础的东西,自己画图理解下)。

handler.sendMessage()-->handler.sendMessageDelayed()-->handler.sendMessageAtTime()-->msg.target = this;queue.enqueueMessage==>msg添加到消息队列中

3.handleMessage(msg)

onStartCommand()执行完毕后我们的Service中的方法就执行完毕了,那么handleMessage()是怎么调用的呢?在前面分析的loop()方法中,我们当时不知道msgtarget字段代码什么,通过上面分析现在我们知道它代表ServiceHandler对象,msg.target.dispatchMessage(msg);则表示执行ServiceHandler对象中的dispatchMessage()方法:

view plain
  1. publicvoiddispatchMessage(Messagemsg){
  2. if(msg.callback!=null){
  3. handleCallback(msg);
  4. }else{
  5. if(mCallback!=null){
  6. if(mCallback.handleMessage(msg)){
  7. return;
  8. }
  9. }
  10. handleMessage(msg);
  11. }
  12. }

该方法首先判断callback是否为空,我们跟踪的过程中未见给其赋值,因此callback字段为空,所以最终将会执行handleMessage()方法,也就是我们ServiceHandler类中复写的方法。在该方法将根据what字段的值判断执行哪段代码。

至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱中。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。


源码下载:Handler的消息机制

更多相关文章

  1. 空对象引用错误:使用SQLite的Android Studio
  2. Android通过反射打造可以存储任何对象的万能SharedPreferences
  3. Android-SDK-Manager 不能更新最有效的解决方法
  4. [置顶] android MultiDex multidex原理下超出方法数的限
  5. android 关于读取SD卡或者U盘的一些方法
  6. 【Android笔记】Activity涉及界面全屏的方法
  7. Android中WebView实现Javascript调用Java类方法
  8. 由Android架构图所想到的Android开发学习方向方法
  9. SharedPreference.Editor的apply和commit方法异同

随机推荐

  1. android 打开 url 方式
  2. Android PDF 阅读器源码
  3. Android通过摇晃手机的频率来控制声音的
  4. Android View onMeasure 方法
  5. Android 让TextView变成Dialog
  6. Android系统开发之修改调试SystemUI
  7. popWindowdemo
  8. Activity四种启动模式之singleTask应用
  9. Android: Actions for BroadcastReceiver
  10. Android MediaExtractor 浅析