消息出队

MessageQueue封装了以单向列表实现的Message队列。在Looper循环中,通过调用MessageQueue的next()方法将队首元素出队进行处理:

  1 Message next() {
  2     // Return here if the message loop has already quit and been disposed.
  3     // This can happen if the application tries to restart a looper after quit
  4     // which is not supported.
  5     final long ptr = mPtr;
  6     if (ptr == 0) {
  7         return null;
  8     }
  9
 10     int pendingIdleHandlerCount = -1; // -1 only during first iteration
 11     int nextPollTimeoutMillis = 0;
 12     for (;;) {
 13         if (nextPollTimeoutMillis != 0) {
 14             Binder.flushPendingCommands();
 15         }
 16
 17         nativePollOnce(ptr, nextPollTimeoutMillis);
 18
 19         synchronized (this) {
 20             // Try to retrieve the next message.  Return if found.
 21             final long now = SystemClock.uptimeMillis();
 22             Message prevMsg = null;
 23             Message msg = mMessages;
 24             if (msg != null && msg.target == null) {
 25                 // Stalled by a barrier.  Find the next asynchronous message in the queue.
 26                 do {
 27                     prevMsg = msg;
 28                     msg = msg.next;
 29                 } while (msg != null && !msg.isAsynchronous());
 30             }
 31             if (msg != null) {
 32                 if (now < msg.when) {
 33                     // Next message is not ready.  Set a timeout to wake up when it is ready.
 34                     nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
 35                 } else {
 36                     // Got a message.
 37                     mBlocked = false;
 38                     if (prevMsg != null) {
 39                         prevMsg.next = msg.next;
 40                     } else {
 41                         mMessages = msg.next;
 42                     }
 43                     msg.next = null;
 44                     if (DEBUG) Log.v(TAG, "Returning message: " + msg);
 45                     msg.markInUse();
 46                     return msg;
 47                 }
 48             } else {
 49                 // No more messages.
 50                 nextPollTimeoutMillis = -1;
 51             }
 52
 53             // Process the quit message now that all pending messages have been handled.
 54             if (mQuitting) {
 55                 dispose();
 56                 return null;
 57             }
 58
 59             // If first time idle, then get the number of idlers to run.
 60             // Idle handles only run if the queue is empty or if the first message
 61             // in the queue (possibly a barrier) is due to be handled in the future.
 62             if (pendingIdleHandlerCount < 0
 63                     && (mMessages == null || now < mMessages.when)) {
 64                 pendingIdleHandlerCount = mIdleHandlers.size();
 65             }
 66             if (pendingIdleHandlerCount <= 0) {
 67                 // No idle handlers to run.  Loop and wait some more.
 68                 mBlocked = true;
 69                 continue;
 70             }
 71
 72             if (mPendingIdleHandlers == null) {
 73                 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
 74             }
 75             mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
 76         }
 77
 78         // Run the idle handlers.
 79         // We only ever reach this code block during the first iteration.
 80         for (int i = 0; i < pendingIdleHandlerCount; i++) {
 81             final IdleHandler idler = mPendingIdleHandlers[i];
 82             mPendingIdleHandlers[i] = null; // release the reference to the handler
 83
 84             boolean keep = false;
 85             try {
 86                 keep = idler.queueIdle();
 87             } catch (Throwable t) {
 88                 Log.wtf(TAG, "IdleHandler threw exception", t);
 89             }
 90
 91             if (!keep) {
 92                 synchronized (this) {
 93                     mIdleHandlers.remove(idler);
 94                 }
 95             }
 96         }
 97
 98         // Reset the idle handler count to 0 so we do not run them again.
 99         pendingIdleHandlerCount = 0;
100
101         // While calling an idle handler, a new message could have been delivered
102         // so go back and look again for a pending message without waiting.
103         nextPollTimeoutMillis = 0;
104     }
105 }

当队首元素执行时间未 或 队首元素为SyncBarrier且队列中没有asynchronous的Message 或 队列为空时,会执行IdleHandler(通过addIdleHandler和removeIdleHandler进行添加和移除)的queueIdle回调或者睡眠(nativePollOnce)。nativePollOnce与OnFileDescriptorEventListener相关,底层是基于Linux的epoll实现的,具体能用来干啥笔者也并不清楚,此处就把它当作定时睡眠操作吧。

第24行,对msg.target判空,通过postSyncBarrier发送的Message的target字段为空,即当队首Message是SyncBarrier时,会在队列中查找asynchronous的Message进行处理,而非asynchronous的Message将被阻塞。

第54行,若mQuitting为真,则返回null,从而导致Looper退出循环,结束Looper的执行。

消息入队

向Handler上发送消息,最终都通过enqueueMessage放入MessageQueue队列中:

 1 boolean enqueueMessage(Message msg, long when) {
 2     if (msg.target == null) {
 3         throw new IllegalArgumentException("Message must have a target.");
 4     }
 5     if (msg.isInUse()) {
 6         throw new IllegalStateException(msg + " This message is already in use.");
 7     }
 8
 9     synchronized (this) {
10         if (mQuitting) {
11             IllegalStateException e = new IllegalStateException(
12                     msg.target + " sending message to a Handler on a dead thread");
13             Log.w(TAG, e.getMessage(), e);
14             msg.recycle();
15             return false;
16         }
17
18         msg.markInUse();
19         msg.when = when;
20         Message p = mMessages;
21         boolean needWake;
22         if (p == null || when == 0 || when < p.when) {
23             // New head, wake up the event queue if blocked.
24             msg.next = p;
25             mMessages = msg;
26             needWake = mBlocked;
27         } else {
28             // Inserted within the middle of the queue.  Usually we don't have to wake
29             // up the event queue unless there is a barrier at the head of the queue
30             // and the message is the earliest asynchronous message in the queue.
31             needWake = mBlocked && p.target == null && msg.isAsynchronous();
32             Message prev;
33             for (;;) {
34                 prev = p;
35                 p = p.next;
36                 if (p == null || when < p.when) {
37                     break;
38                 }
39                 if (needWake && p.isAsynchronous()) {
40                     needWake = false;
41                 }
42             }
43             msg.next = p; // invariant: p == prev.next
44             prev.next = msg;
45         }
46
47         // We can assume mPtr != 0 because mQuitting is false.
48         if (needWake) {
49             nativeWake(mPtr);
50         }
51     }
52     return true;
53 }

enqueueMessage通过when字段维护队列中Message的先后循序。

第22行,当队列为空 或 when为0 或 when小于队首元素的when,则将Message放入队首。通过Handler的postAtFrontOfQueue和sendMessageAtFrontOfQueue提交的Runnable和Message满足when为0。

消息移除

removeMessages(Handler h, int what, Object object)、removeMessages(Handler h, Runnable r, Object object)、removeCallbacksAndMessages(Handler h, Object object)用于移除消息。这三个函数底层实现基本类似,分两步操作:1) 循环移除满足条件的Message,直到其不位于队首;2) 移除剩余的满足条件Message:

void removeMessages(Handler h, int what, Object object) {if (h == null) {return;}synchronized (this) {Message p = mMessages;// Remove all messages at front.while (p != null && p.target == h && p.what == what&& (object == null || p.obj == object)) {Message n = p.next;mMessages = n;p.recycleUnchecked();p = n;}// Remove all messages after front.while (p != null) {Message n = p.next;if (n != null) {if (n.target == h && n.what == what&& (object == null || n.obj == object)) {Message nn = n.next;n.recycleUnchecked();p.next = nn;continue;}}p = n;}}
}

转载于:https://www.cnblogs.com/moderate-fish/p/7639029.html

Android源码学习(3) Handler之MessageQueue相关推荐

  1. Android源码学习之handler

    前言 是滴!我又来了...今天来讲讲老少皆宜的大名鼎鼎的handler.是的,想必handler这个东西已经被讨论的天花乱坠了,也经常被我们用在实际开发中,但是其中很多细节知识还是值得我们去学习深究的 ...

  2. android源码学习-Toast实现原理讲解

    前言: 前些日志QQ群有朋友发了一个Toast的崩溃日志.Toast如此简单的用法怎么会崩溃呢?所以顺便就学习了一下Toast在源码中的实现,不算复杂,但内容挺多的,这里就来分享一下,方便读者. 一. ...

  3. Android源码学习之浅析SystemServer脉络

    在之前的博文中<Android源码学习之如何创建使用JNI>和<Android源码学习之如何使用eclipse+NDK>中,浅谈了如何创建使用JNI和如何利用NDK工具开发创建 ...

  4. 【Android 源码学习】 init启动

    目录 Android 源码学习 init启动 从main.cpp开始 init.cpp 部分逻辑 init启动zygote 属性服务 总结 Android 源码学习 init启动 Android 11 ...

  5. 【Android 源码学习】Zygote启动原理

    Android 源码学习 Zygote启动原理 望舒课堂 Zygote进程启动原理学习记录整理. Zygote简介 Zygote是进程在init进程启动时创建的,进程本身是app_process,来源 ...

  6. 【Android 源码学习】系统架构和启动流程

    Android 源码学习 系统架构和启动流程 望舒课堂 学习记录整理.以及以下参考文章的整理汇总.便于我个人的学习记录. 感谢IngresGe,Gityuan的精彩文章.为我们这些初探android系 ...

  7. 【Android 源码学习】SystemServer启动原理

    Android 源码学习 SystemServer启动原理 望舒课堂 SystemServer进程启动原理学习记录整理. 参考文章: Android系统启动流程(三)解析SyetemServer进程启 ...

  8. 【Android 源码学习】SharedPreferences 源码学习

    第一章:SharedPreferences 源码学习 文章目录 第一章:SharedPreferences 源码学习 Android SharedPreferences的缺陷 MMKV.Jetpack ...

  9. Android源码学习之工厂方法模式应用

    主要内容: 工厂方法模式定义 工厂方法模式优势 工厂方法模式在Android源码中的应用 一.工厂方法模式定义 工厂方法模式定义: Define an interface for creating a ...

最新文章

  1. Centos6下安装中文字体
  2. ae万能弹性表达式_外置常用ae插件 快速掌握AE软件的精髓
  3. Spring------自动化装配Bean(一)
  4. NSString 字符串 操作 常用
  5. 万用表测线路断点位置_如何测出电线电缆断点在哪?来看看常见的7种方法
  6. mysql的sum函数 如何设置默认值_mysql使用sum()出现null的问题,各种总结
  7. 为用户设计的产品,就应该用用户熟悉的语言
  8. oracle進程時高時低,oracle低權限下獲取shell
  9. Linux /etc/login.defs配置文件
  10. CoffeeScript 更优美的Javascript
  11. php post 漏洞_漏洞研究|ThinkPHP request函数远程代码执行
  12. Pitch Innovations音频插件合集
  13. 全屋WiFi方案:Mesh路由器组网和AC+AP
  14. HTML深海骑兵制作,深海迷航代码独眼巨人号护盾发生器 | 手游网游页游攻略大全...
  15. 美团O2O供应链系统架构设计解析
  16. Python-生成gif图片验证码
  17. 取回Apple TV遥控器的D-Pad
  18. 华为开发者大赛-昇腾AI初创大赛决赛暨星火计划Online第二期来啦!
  19. 详解 WebSocket 原理,附完整的聊天室实战 Demo
  20. 【python基础】python中常用字符串函数详解

热门文章

  1. 各类神经网络知识收集
  2. SI 和 DI 寄存器的区别
  3. Plugin with id ‘com.android.XXX‘ not found.
  4. vue中echarts 5.0版本以上不支持因为官方移除了地图数据和map文件夹
  5. 手把手带你撸深度学习经典模型(一)----- UNet
  6. Android热修复技术原理详解(最新最全版本)
  7. Google Project Zero挖洞经验整理
  8. Java锁 (概览)
  9. 【问链-链改进行时】 第二课 链改的技术架构选择
  10. 网站假设php,PHP个人网站架设连环讲(一)