一、简介
消息处理机制主要涉及到这几个类:
1.Looper
2.MessageQueue
3.Message
4.Handler

二、源码分析

Looper.class的关键源码:

  1. //保存Looper对象,在android中每创建一个消息队列,就有一个并且是唯一一个与之对应的Looper对象
  2. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  3. //主线程的Looper
  4. private static Looper sMainLooper;
  5. //消息队列
  6. final MessageQueue mQueue;
  7. final Thread mThread;
  8.  
  9. //子线程中通过调用该方法来创建消息队列
  10. public static void prepare() {
  11. prepare(true);
  12. }
  13.  
  14. private static void prepare(boolean quitAllowed) {
  15. if (sThreadLocal.get() != null) {
  16. throw new RuntimeException("Only one Looper may be created per thread");
  17. }
  18. sThreadLocal.set(new Looper(quitAllowed));
  19. }
  20.  
  21. //主线程调用该方法来创建消息队列
  22. public static void prepareMainLooper() {
  23. prepare(false);
  24. synchronized (Looper.class) {
  25. if (sMainLooper != null) {
  26. throw new IllegalStateException("The main Looper has already been prepared.");
  27. }
  28. sMainLooper = myLooper();
  29. }
  30. }
  31.  
  32. //实例化Looper,创建消息队列,获取当前线程
  33. private Looper(boolean quitAllowed) {
  34. mQueue = new MessageQueue(quitAllowed);
  35. mThread = Thread.currentThread();
  36. }
  37.  
  38. //调用loop方法开启消息循环
  39. public static void loop() {
  40. //获取当前的Looper对象,若为null,抛出异常
  41. final Looper me = myLooper();
  42. if (me == null) {
  43. throw new RuntimeException("No Looper; Looper.prepare()
  44. wasn't called on this thread.");
  45. }
  46. //获取当前的消息队列,进入循环
  47. final MessageQueue queue = me.mQueue;
  48. for (;;) {
  49. //调用next()方法从消息队列中获取消息,如果为null,结束循环;否则,继续执行(有可能会阻塞)
  50. Message msg = queue.next();
  51. if (msg == null) {
  52. return;
  53. }
  54. ......
  55. try {
  56. //调用handler的dispatchMessage(msg)分发消息
  57. msg.target.dispatchMessage(msg);
  58. } finally {
  59. ......
  60. }
  61. //回收消息资源
  62. msg.recycleUnchecked();
  63. }
  64. }
  65.  
  66. //消息循环退出
  67. public void quit() {
  68. mQueue.quit(false);
  69. }
  70.  
  71. public void quitSafely() {
  72. mQueue.quit(true);
  73. }

消息循环退出过程

从上面可以看到loop()方法是一个死循环,只有当MessageQueue的next()方法返回null时才会结束循环。那么MessageQueue的next()方法何时为null呢?

在Looper类中我们看到了两个结束的方法quit()和quitSalely()。
两者的区别就是quit()方法直接结束循环,处理掉MessageQueue中所有的消息。
quitSafely()在处理完消息队列中的剩余的非延时消息(延时消息(延迟发送的消息)直接回收)时才退出。这两个方法都调用了MessageQueue的quit()方法

MessageQueue.class 的关键源码:

MessageQueue中最重要的就是两个方法:
1.enqueueMessage()向队列中插入消息
2.next() 从队列中取出消息

  1. /*
  2. *MessageQueue中enqueueMessage方法的目的有两个:
  3. *1.插入消息到消息队列
  4. *2.唤醒Looper中等待的线程(如果是即时消息并且线程是阻塞状态)
  5. */
  6. boolean enqueueMessage(Message msg, long when) {
  7. //发送该消息的handler为null,抛出异常
  8. if (msg.target == null) {
  9. throw new IllegalArgumentException("Message must have a target.");
  10. }
  11. //此消息正在被使用
  12. if (msg.isInUse()) {
  13. throw new IllegalStateException(msg + " This message is already in use.");
  14. }
  15.  
  16. synchronized (this) {
  17. //此消息队列已经被放弃了
  18. if (mQuitting) {
  19. IllegalStateException e = new IllegalStateException(
  20. msg.target + " sending message to a Handler on a dead thread");
  21. msg.recycle();
  22. return false;
  23. }
  24. msg.markInUse();
  25. msg.when = when;
  26. //消息队列的第一个元素,MessageQueue中的成员变量mMessages指向的就是该链表的头部元素。
  27. Message p = mMessages;
  28. boolean needWake;
  29. if (p == null || when == 0 || when < p.when) {
  30. //如果此队列中头部元素是null(空的队列,一般是第一次),或者此消息不是延时的消息,则此消息需要被立即处理,
  31. //将该消息作为新的头部,并将此消息的next指向旧的头部。如果是阻塞状态则需要唤醒。
  32. msg.next = p;
  33. mMessages = msg;
  34. needWake = mBlocked;
  35. } else {
  36. //如果此消息是延时的消息,则将其添加到队列中,
  37. //原理就是链表的添加新元素,按照时间顺序来插入的,这样就得到一条有序的延时消息链表
  38. needWake = mBlocked && p.target == null && msg.isAsynchronous();
  39. Message prev;
  40. for (;;) {
  41. prev = p;
  42. p = p.next;
  43. if (p == null || when < p.when) {
  44. break;
  45. }
  46. if (needWake && p.isAsynchronous()) {
  47. needWake = false;
  48. }
  49. }
  50. msg.next = p;
  51. prev.next = msg;
  52. }
  53. if (needWake) {
  54. nativeWake(mPtr);
  55. }
  56. }
  57. return true;
  58. }
  59.  
  60. Message next() {
  61. //与native方法相关,当mPtr为0时返回null,退出消息循环
  62. final long ptr = mPtr;
  63. if (ptr == 0) {
  64. return null;
  65. }
  66.  
  67. int pendingIdleHandlerCount = -1;
  68. //0不进入睡眠,-1进入睡眠
  69. int nextPollTimeoutMillis = 0;
  70. for (;;) {
  71. if (nextPollTimeoutMillis != 0) {
  72. //处理当前线程中待处理的Binder进程间通信请求
  73. Binder.flushPendingCommands();
  74. }
  75. //native方法,nextPollTimeoutMillis为-1时进入睡眠状态
  76. //阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
  77. //如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
  78. //如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
  79. //如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回
  80. nativePollOnce(ptr, nextPollTimeoutMillis);
  81. synchronized (this) {
  82. final long now = SystemClock.uptimeMillis();
  83. Message prevMsg = null;
  84. Message msg = mMessages;
  85. if (msg != null && msg.target == null) {
  86. do {
  87. prevMsg = msg;
  88. msg = msg.next;
  89. } while (msg != null && !msg.isAsynchronous());
  90. }
  91. if (msg != null) {
  92. if (now < msg.when) {
  93. // Next message is not ready. Set a timeout to wake up when it is ready.
  94. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
  95. } else {
  96. //正常取出消息,设置mBlocked = false代表目前没有阻塞
  97. mBlocked = false;
  98. if (prevMsg != null) {
  99. prevMsg.next = msg.next;
  100. } else {
  101. mMessages = msg.next;
  102. }
  103. msg.next = null;
  104. msg.markInUse();
  105. return msg;
  106. }
  107. } else {
  108. // No more messages.更新到睡眠状态
  109. nextPollTimeoutMillis = -1;
  110. }
  111.  
  112. // Process the quit message now that all pending messages have been handled.
  113. if (mQuitting) {
  114. dispose();
  115. return null;
  116. }
  117.  
  118. // If first time idle, then get the number of idlers to run.
  119. // Idle handles only run if the queue is empty or if the first message
  120. // in the queue (possibly a barrier) is due to be handled in the future.
  121. if (pendingIdleHandlerCount < 0
  122. && (mMessages == null || now < mMessages.when)) {
  123. pendingIdleHandlerCount = mIdleHandlers.size();
  124. }
  125. if (pendingIdleHandlerCount <= 0) {
  126. // No idle handlers to run. Loop and wait some more.
  127. mBlocked = true;
  128. continue;
  129. }
  130.  
  131. if (mPendingIdleHandlers == null) {
  132. mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
  133. }
  134. mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
  135. }
  136. }
  137. //非睡眠状态下处理IdleHandler接口
  138. for (int i = 0; i < pendingIdleHandlerCount; i++) {
  139. final IdleHandler idler = mPendingIdleHandlers[i];
  140. // release the reference to the handler
  141. mPendingIdleHandlers[i] = null;
  142. boolean keep = false;
  143. try {
  144. keep = idler.queueIdle();
  145. } catch (Throwable t) {
  146. Log.wtf(TAG, "IdleHandler threw exception", t);
  147. }
  148. if (!keep) {
  149. synchronized (this) {
  150. mIdleHandlers.remove(idler);
  151. }
  152. }
  153. }
  154. pendingIdleHandlerCount = 0;
  155. nextPollTimeoutMillis = 0;
  156. }
  157. }

Handler.class源码分析:

  1. /*
  2. *通过handler类向线程的消息队列发送消息,
  3. *每个Handler对象中都有一个Looper对象和MessageQueue对象
  4. */
  5. public Handler(Callback callback, boolean async) {
  6. if (FIND_POTENTIAL_LEAKS) {
  7. final Class<? extends Handler> klass = getClass();
  8. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
  9. (klass.getModifiers() & Modifier.STATIC) == 0) {
  10. Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
  11. klass.getCanonicalName());
  12. }
  13. }
  14. //获取Looper对象
  15. mLooper = Looper.myLooper();
  16. if (mLooper == null) {...}
  17. //获取消息队列
  18. mQueue = mLooper.mQueue;
  19. mCallback = callback;
  20. mAsynchronous = async;
  21. }
  22.  
  23. /*
  24. *多种sendMessage方法,最终都调用了同一个方法sendMessageAtTime()
  25. */
  26. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  27. MessageQueue queue = mQueue;
  28. if (queue == null) {
  29. RuntimeException e = new RuntimeException(
  30. this + " sendMessageAtTime() called with no mQueue");
  31. Log.w("Looper", e.getMessage(), e);
  32. return false;
  33. }
  34. //向消息队列中添加消息
  35. return enqueueMessage(queue, msg, uptimeMillis);
  36. }
  37.  
  38. /*
  39. *1.当Message中的callback不为null时,执行Message中的callback中的方法。这个callback时一个Runnable接口。
  40. *2.当Handler中的Callback接口不为null时,执行Callback接口中的方法。
  41. *3.直接执行Handler中的handleMessage()方法。
  42. */
  43. public void dispatchMessage(Message msg) {
  44. // 消息Callback接口不为null,执行Callback接口
  45. if (msg.callback != null) {
  46. handleCallback(msg);
  47. } else {
  48. if (mCallback != null) {
  49. //Handler Callback接口不为null,执行接口方法
  50. if (mCallback.handleMessage(msg)) {
  51. return;
  52. }
  53. }
  54. //处理消息
  55. handleMessage(msg);
  56. }
  57. }

android消息处理源码分析的更多相关文章

  1. Android HandlerThread 源码分析

    HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我又有一 个耗时任务需要执行,我们不得不重新创建线 ...

  2. Android Choreographer 源码分析

    Choreographer 的作用主要是配合 Vsync ,给上层 App 的渲染提供一个稳定的 Message 处理的时机,也就是 Vsync 到来的时候 ,系统通过对 Vsync 信号周期的调整, ...

  3. Appium Android Bootstrap源码分析之启动运行

    通过前面的两篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>和<Appium Android Bootstrap源码分析之命令解析 ...

  4. Appium Android Bootstrap源码分析之命令解析执行

    通过上一篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在b ...

  5. Appium Android Bootstrap源码分析之控件AndroidElement

    通过上一篇文章<Appium Android Bootstrap源码分析之简介>我们对bootstrap的定义以及其在appium和uiautomator处于一个什么样的位置有了一个初步的 ...

  6. Appium Android Bootstrap源码分析之简介

    在上一个系列中我们分析了UiAutomator的核心源码,对UiAutomator是怎么运行的原理有了根本的了解.今天我们会开始另外一个在安卓平台上基于UiAutomator的新起之秀--Appium ...

  7. Android base-adapter-helper 源码分析与扩展

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/44014941,本文出自:[张鸿洋的博客] 本篇博客是我加入Android 开源项 ...

  8. Android -- ViewGroup源码分析+自定义

    1,我们前三篇博客了解了一下自定义View的基本方法和流程 从源码的角度一步步打造自己的TextView 深入了解自定义属性 onMeasure()源码分析 之前,我们只是学习过自定义View,其实自 ...

  9. [旧][Android] Retrofit 源码分析之 ServiceMethod 对象

    备注 原发表于2016.05.03,资料已过时,仅作备份,谨慎参考 前言 大家好,我又来学习 Retrofit 了,可能这是最后一篇关于 Retrofit 框架的文章了.我发现源码分析这回事,当时看明 ...

随机推荐

  1. textarea 里设置 style="resize:none"

    禁止textarea拉伸的方法是::                                    设置这个 style="resize:none" 属性 例子: < ...

  2. PAT1129:Recommendation System

    1129. Recommendation System (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue ...

  3. MQ、JMS 关系的理解

    MQ简介: MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接它们.消息传 ...

  4. Linux文本处理命令 -- awk

    简介 awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再 ...

  5. 注解@PostConstruct与@PreDestroy 特性说明

    简介 Java EE5 引入了@PostConstruct和@PreDestroy这两个作用于Servlet生命周期的注解,实现Bean初始化之前和销毁之前的自定义操作.此文主要说明@PostCons ...

  6. IntelliJ IDEA中 todo的使用

    在代码的注释部分加入TODO 大小写忽略,如下图所示 查看项目中有哪些待办项,所下图所示

  7. 你不知道的JavaScript--Item28 垃圾回收机制与内存管理

    1.垃圾回收机制-GC Javascript具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存. 原理:垃圾收集器会定期(周期性 ...

  8. HTML5 CSS3 专题 :诱人的实例 3D旋转木马效果相册

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/32964301 首先说明一下创意的出处:http://www.zhangxinxu ...

  9. 玩转SSH--Hibernate(三)---手动修改数据库,前台查询信息不同步更新问题解决方法

    在用hibernate时遇到一个挺纠结的问题,就是我在手动修改数据库的信息后,前台页面查询到的信息还是之前的结果,一开始以为是缓存的问题,经过多次修改和在网上查询资料,最终发现可能是hibernate ...

  10. Python 枚举

    1. 枚举的定义 首先,定义枚举要导入enum模块.枚举定义用class关键字,继承Enum类.用于定义枚举的class和定义类的class是有区别. 示例代码: from enum import E ...