Android源码学习(3) Handler之MessageQueue
消息出队
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相关推荐
- Android源码学习之handler
前言 是滴!我又来了...今天来讲讲老少皆宜的大名鼎鼎的handler.是的,想必handler这个东西已经被讨论的天花乱坠了,也经常被我们用在实际开发中,但是其中很多细节知识还是值得我们去学习深究的 ...
- android源码学习-Toast实现原理讲解
前言: 前些日志QQ群有朋友发了一个Toast的崩溃日志.Toast如此简单的用法怎么会崩溃呢?所以顺便就学习了一下Toast在源码中的实现,不算复杂,但内容挺多的,这里就来分享一下,方便读者. 一. ...
- Android源码学习之浅析SystemServer脉络
在之前的博文中<Android源码学习之如何创建使用JNI>和<Android源码学习之如何使用eclipse+NDK>中,浅谈了如何创建使用JNI和如何利用NDK工具开发创建 ...
- 【Android 源码学习】 init启动
目录 Android 源码学习 init启动 从main.cpp开始 init.cpp 部分逻辑 init启动zygote 属性服务 总结 Android 源码学习 init启动 Android 11 ...
- 【Android 源码学习】Zygote启动原理
Android 源码学习 Zygote启动原理 望舒课堂 Zygote进程启动原理学习记录整理. Zygote简介 Zygote是进程在init进程启动时创建的,进程本身是app_process,来源 ...
- 【Android 源码学习】系统架构和启动流程
Android 源码学习 系统架构和启动流程 望舒课堂 学习记录整理.以及以下参考文章的整理汇总.便于我个人的学习记录. 感谢IngresGe,Gityuan的精彩文章.为我们这些初探android系 ...
- 【Android 源码学习】SystemServer启动原理
Android 源码学习 SystemServer启动原理 望舒课堂 SystemServer进程启动原理学习记录整理. 参考文章: Android系统启动流程(三)解析SyetemServer进程启 ...
- 【Android 源码学习】SharedPreferences 源码学习
第一章:SharedPreferences 源码学习 文章目录 第一章:SharedPreferences 源码学习 Android SharedPreferences的缺陷 MMKV.Jetpack ...
- Android源码学习之工厂方法模式应用
主要内容: 工厂方法模式定义 工厂方法模式优势 工厂方法模式在Android源码中的应用 一.工厂方法模式定义 工厂方法模式定义: Define an interface for creating a ...
最新文章
- Centos6下安装中文字体
- ae万能弹性表达式_外置常用ae插件 快速掌握AE软件的精髓
- Spring------自动化装配Bean(一)
- NSString 字符串 操作 常用
- 万用表测线路断点位置_如何测出电线电缆断点在哪?来看看常见的7种方法
- mysql的sum函数 如何设置默认值_mysql使用sum()出现null的问题,各种总结
- 为用户设计的产品,就应该用用户熟悉的语言
- oracle進程時高時低,oracle低權限下獲取shell
- Linux /etc/login.defs配置文件
- CoffeeScript 更优美的Javascript
- php post 漏洞_漏洞研究|ThinkPHP request函数远程代码执行
- Pitch Innovations音频插件合集
- 全屋WiFi方案:Mesh路由器组网和AC+AP
- HTML深海骑兵制作,深海迷航代码独眼巨人号护盾发生器 | 手游网游页游攻略大全...
- 美团O2O供应链系统架构设计解析
- Python-生成gif图片验证码
- 取回Apple TV遥控器的D-Pad
- 华为开发者大赛-昇腾AI初创大赛决赛暨星火计划Online第二期来啦!
- 详解 WebSocket 原理,附完整的聊天室实战 Demo
- 【python基础】python中常用字符串函数详解
热门文章
- 各类神经网络知识收集
- SI 和 DI 寄存器的区别
- Plugin with id ‘com.android.XXX‘ not found.
- vue中echarts 5.0版本以上不支持因为官方移除了地图数据和map文件夹
- 手把手带你撸深度学习经典模型(一)----- UNet
- Android热修复技术原理详解(最新最全版本)
- Google Project Zero挖洞经验整理
- Java锁 (概览)
- 【问链-链改进行时】 第二课 链改的技术架构选择
- 网站假设php,PHP个人网站架设连环讲(一)