转载请注明出处:http://blog.csdn.net/fishle123/article/details/47864923

对Android开发有一定了解的人都知道,Android应用程序是消息驱动的。Android应用程序的消息机制是围绕消息队列来实现的,具体的,主要通过HandlerMessageQueueLooper三个类来实现。其中Handler用来发送和处理消息;MessageQueue表示一个消息队列,负责入队和出队消息;Looper类用于创建消息循环。本文结合源码来介绍Android的消息机制包括:消息循环的创建、消息的发送和处理。

每个Android应用程序都一个主线程ActivityThread,每个主线程都有一个消息循环。先看一下ActivityThreadmain方法:

可以看到第22行调用Looper.prepareMainLooper(),再看一下prepareMainLooper具体做了哪些工作:

可以看到在第调用prepare,然后调用setMainLooper设置主线程的Looper。不难推测,prepare中会创建Looper,先贴上代码:

prepare()里面先检查是否已经创建了Looper,如果没有,则创建一个并保存到sThreadLocal中,反之,如果已经创建了一个,则会抛出异常。通过这里,我们也可以发现,prepare只能调用一次,每个线程只有一个Looper。那这里已经创建了Looper,那么第36行的Looper.loop()又是做什么的呢?继续看源码:

可以看到,在loop中会进入一个死循环,每次循环的开始调用MessageQueu.next()方法从消息队列中取出一个Message,然后调用msg.target.dispatchMessage(msg)交给message的接受者处理。这里的msg.target就是message的接受者,其实就是一个Handler。如果消息队列为空,那么MessageQueue.next()会阻塞,直到有新的消息达到。到这里,就可以明白调用Looper.loop后,线程就进入了消息循环等待message到来并交给message对应的Handler来处理消息。正常情况下,一旦进入消息循环就不会退出,除非MessageMessage.target=null,见第16行代码。

到此为止,我们就知道如果要给线程创建一个消息循环,可以按如下方式来定义:

上面提到在Looper中会通过msg.target.dispatchMessage(msg)将msg分派到对应的Handler来出来,这里看一下dispatchMessage的实现:

dispatchMessage中先检查在Message中是否定义了消息的callback,如果有,优先调用Messagecallback来处理消息;反之,如果HandlermCallBack不为null,则调用mCallBackhandleMessage来处理,当MCallBack也为null的时候,才调用HanddlerhandleMessage来处理。这里可以看出,Message有三种方式来实现它的处理逻辑:

1)在Message.callback中实现,这里的callback是一个Runnable对象;

2)在Handler的内部接口CallBack中实现;

3)重写Handler的handleMessage来实现。

阅读Handler的源码,可以发现当调用handler.post(Runnable)的时候,就是通过第1种方式来实现:

通常我们会通过第3种方式来定义自己的Handler,如在主线程:

这里看一下Handler的构造函数(还有其他几种构造函数):

这里可以看到,创建Handler的时候,会在保存当前线程的Looper及Looper对应的MessageQueue。这样发送消息时Handler就能将Message发送到当前线程的消息循环中。当然,我们也可以指定Looper,这样就可以给另一个线程发送消息,如在另一个线程中给主线程发送消息。这里也可以看出,定义Handler之前一定要先调用Looper.prepare()来创建一个Looper,主线程中会在ActivityThread的main方法中创建,不需要我们手动调用。但是在子线程中,一定要先调用Looper.prepare()才能创建Handler,否则会抛出一个异常,如第78行所示。

Handler常见的用法之一就是在子线程中更新UI,我们可以通过handler.post(Runnable)让runnable在主线程中执行。其中Activity的runOnUIThread就是利用handler来实现的,view.post(Runnable)也是相似的实现。


当调用handler.sendMessage()发送消息时,最终会调用Handler.sendMessageAtTime,源码如下:

在这里可以看出,发送消息的时候,将Message添加到队列中,并且会将Message的接受者设置为发送Message的handler,如代码第6行所示。前面有提到,在loop中会调用MessageQueue.next()来取消息。这里先看一下消息的入队操作:

看代码的第2935行代码,这里会根据消息的处理时间Message.when来确定消息插入到队列中的具体位置,从而保证队首的消息是下一个要处理的。在这里也可以发现,主线程的消息循环是不能退出的,如第6行所示。

前面有提到MessageQueue.next()可能会阻塞。

这里一起看一下消息的出队操作:


在第3行,定义变量nextPollTimeoutMills,用来描述当消息队列中没有新的消息时,当前线程需要睡眠多长时间。当nextPollTimeoutMills等于-1时,表示无限制等待,直到有新的消息到达。在没有消息到达的时候会调用nativePollOnce来检查是否有新消息需要处理,nativePollOnce是一个native函数,主要是通过Linux中的poll函数来实现的。

当队列不为空时,在第17行先判断当前时间是否大于等于message的处理时间,如果是,则返回该message,反之,计算需要等待的时间,并更新nextPollTimeoutMills

如果队列为空,则nextPollTimeoutMills的值设置为-1。在进入睡眠之前,先调用注册到消息队列中的空闲消息处理器IdleHandler对象的queueIdle函数,以便有机会在线程空闲时执行一些操作。可以通过MessageQueueaddIdleHandlerremoveIdleHandler来注册和注销一个空闲消息处理器。


到此为止,已经分析完消息循环的创建,发送消息,以及消息的处理。

更多相关文章

  1. 系出名门Android(10) - HTTP 通信, XML 解析, 通过 Hander 实现
  2. Android(安卓)Keyboard/Touch Panel分析
  3. android 按键
  4. Android(安卓)屏幕切换和onConfigurationChanged不调用的原因
  5. android AsyncTask学习
  6. Android消息总线LiveDataBus
  7. Android横竖屏切换小结
  8. AndroidMenifest.xml中android:sharedUserId="android.uid.syste
  9. 带着问题学习 Android(安卓)Handler 消息机制

随机推荐

  1. 如何提交位于内的输入?
  2. (android 实战总结)android对html支持接
  3. 仅使用CSS的输入字段的一部分
  4. 用于在表中强制换行的PHP或HTML/CSS解决
  5. html解析类库htmlparser.net使用方法
  6. 文本输入占位符不在IE和Firefox中显示
  7. 如何使用struts2访问表单数组字段
  8. HTML5实战与剖析之媒体元素(1、video标签
  9. DiggBar如何基于不在其域上的内容动态调
  10. Smarty快速入门之一