Handler 最为 Android 中最高频使用的消息机制,这些年在面试过程中,碰到的越来越少了。但是一般遇到,就是比较抠实现细节了。

例如我之前文章讲到,Handler 的 runWithScissors() 可以实现 A 线程向 B 线程发送一个消息,使 A 线程进入阻塞,等待 B 线程处理完消息后再继续执行。那么延伸出来的 2 个问题就是,runWithScissors() 的原理是什么?它被标记为 @hide 不允许使用是因为存在什么问题吗?

再比如,我们知道 MessageQueue,实则是一个基于触发时间 when 的优先级消息队列,那么在什么场景下,MQ 中靠后的消息,会先于靠前的消息,被调度执行呢?

这些问题,若是不看源码,只停留在使用层面,基本上很难捋清楚。

我这里整理了关于 Handler 的超过 40+ 各高频的问题,基本涵盖了各个角度,大家可以拿来自测一下,在面试前也可以刷一刷,毕竟 Handler 再低频,遇到就是 100%。


Q:Handler 机制中,存在哪些角色?各自承担了什么功能?

  1. Handler:消息辅助类 & 对外的接口 & 向 MQ 投递消息 & 消息的目标处理者;

  2. Message:消息的载体 & 被 Handler 投递 & 自带 Handler 处理 & 自带消息池;

  3. Looper:循环器 & 持有 MQ & 循环从 MQ 中获取消息 & TLS 线程唯一;

  4. MessageQueue:基于时间的优先级队列 & 链表结构 & Java 与 C++ 层的纽带;

Q:Handler 分发事件优先级,是否可拦截?拦截的优先级如何?

  1. Handler 中,通过 dispatchMessage() 处理消息,其中存在优先级策略;

  2. 优先级1:msg,callback,run() - 独占;

  3. 优先级2:mCallback.handleMessage(msg) - 返回值决定是否拦截该消息;

  4. 优先级3:handle.handleMessage();

Q:主线程 Looper 何时运行?

  1. App 启动时,会调用到 ActivityThread 中,Looper 就在其 main() 方法中被启动;

  2. main() 中会主动调用 Looper.prepareMainLooper() 和 Looper.loop();

  3. Tips:ActivityThread 不继承自 Thread,它只是一个运行在主线程上的对象;

Q:Handler 的 Message 可以分为那 3 类?分别有什么标识?

  1. 同步 Message:普通 Message;

  2. 异步 Message:msg.setAsynchronous(true)

  3. 同步屏障:msg.target == null

Q:同一个 Message 对象能否重复 send?

  1. 关键在于如何定义同一个 Message。

  2. 角度一:Java 对象层面,可被复用;原因:Message 由消息池维护,即同一个对象被回收后会被再次复用;| new Message & Message.obtain()

  3. 角度二:业务层面,不能复用;原因:Message 通过 enqueueMessage() 入队时,会通过 markInUse() 标记,再次入队无法通过 isInUse() 检查,则抛出异常;

Q:场景:MessageQueue 是基于触发时间 when 的优先级队列,那么什么情况下,队列中靠后的消息会优先得到执行?原理是什么?

  1. 场景:靠前的消息是同步消息,靠后的消息是异步消息,且消息队列的队头为同步屏障;

  2. 原理:同步屏障会阻塞 MQ 中的同步消息,优先处理异步消息;

Q:Message 的同步屏障有什么用?有什么意义?如何发送一个同步屏障?

  1. 用途:阻塞 MQ 对同步 Message 的分发,优先处理异步消息,没有异步消息时则进入休眠,直到同步屏障被移除;

  2. 意义:允许异步消息优先于同步消息执行;

  3. 同步屏障:特殊的 Message,target == null,无法通过 Handler 入队出队,需直接操作 MQ;入队:postSyncBarrier():返回一个屏障 token;出队:removeSyncBarrier()

Q:什么是异步消息?如何发送?

  1. 意义:需配合同步屏障使用,否者与同步消息无区别;

  2. 异步消息:setAsynchronous(true) → 向 flags 添加 FLAG_ASYNCHRONOUS 标记

  3. 发送方式 通过异步 Handler 发送 → 构造 Handler 时,async 传递 true 发送消息前,主动调用 setAsynchronous(true)

  4. 安全起见,Android 9.0 普通开发者无法使用异步消息,所有发送方式被标记为 @hide

Q:Handler 的 IdleHandler 机制,如何理解?有什么用途?

  1. 接口,需实现 queueIdle() 方法 & 定义在 MQ 中 & 以 MQ mIdleHandlers 维护存储

  2. 用途:可在 MQ 即将空闲时,处理任务;

  3. 逻辑点:MQ.next() 中,当前无待执行消息时,执行 mIdleHandlers;

  4. 依据 queueIdle() 返回值分:持续回调(true) & 一次性回调(false),false 会导致执行完后,从 mIdleHandlers 中移除

Q:IdleHandler 执行耗时会影响正常的消息分发吗?Handler 内部如何处理?

  1. 会;

  2. IdleHandler 的耗时不可控;

  3. 执行完后会重置 nextPollTimeoutMillis = 0,重新分发最近消息

Q:移除消息的 removeMessage() 为什么需要两次循环?

  1. 优化效率;

  2. while-1:移除消息 & 找到下一个待处理的消息,存入 mMessages 中;

  3. while-2:从 mMessages 开始,移除后续符合条件的消息;

Q:Handler 的 runWithScissors() 可实现 A 线程阻塞等待 B 线程处理完消息后再继续执行的功能,它为什么被标记为 hide?存在什么问题?原因是什么?

  1. 实现:将 Runnable 包装为 BlockingRunnable,其内通过 synchronized + wait 进入等待,待 r 执行完后,调用 notifyAll() 唤醒等待队列的子线程,子线程继续执行;

  2. 问题:在子线程 Looper 中使用,可能导致 A 线程进入 wait 等待,而永远得不到被 notify 唤醒;

  3. 原因:子线程 Looper 允许退出,若包装的 BlockingRunnable 被执行前,MessageQueue 退出,则该 runnable 永远不会被执行,则会导致 A 线程一直处于 wait 等待,永远不会被 notify 唤醒;

Q:Looper.loop 中,如果没有待处理的消息,为什么不会阻塞 UI?

  1. 主线程在 MessageQueue 没有消息时,会阻塞在 loop 的 queue.next() 方法中的 nativePollOnce()方法里。

  2. 此时主线程会释放 CPU 资源进入休眠状态,直到下一个消息到达或者有事务发生,通过往 pipe 管道写端写入数据的方式,来唤醒主线程。这里采用的是 epoll 机制。

  3. epoll 机制是一种 IO 多路复用机制,可以同时监控多个描述符,在有事件发生的时候,立即通知相应程序进行读或写操作,类似一种 callback 的回调机制。

  4. 主线程在大多数时候是处于休眠状态,并不会消耗大量的 CPU 资源。当有新的消息或事务到达时,会立即唤醒主线程进行处理,所以对用户来说是无感知的。

Q:如果 Java 层 MQ 中消息很少,但是响应时间却很长,是什么原因?

  1. MQ 队列中,该 Message 前的 Message 处理较为耗时;

  2. Native 层消息过多,Java 层 MQ 消息优先级最低,最后处理;

Q:Looper 的 Printer 输出的日志,有什么其他用途?依靠的原理是什么?有什么缺点?

  1. 用途:性能监控;

  2. 原理:通过筛选日志内存,区分 Message 的开始执行和结束执行的时间点,即可判断处理 Message 的耗时,即主线程卡顿耗时;

  3. 缺点:Printer 存在大量字符串拼接,在消息量大时,会导致性能受损;| 实测数据:存在 Printer 时,在列表快速滑动时,平均帧率降低 5 帧;

Q:Handler 可以 IPC 通信吗?

  1. 不能;

  2. Handler 只能用于共享内存地址的 2 个线程通信,即同进程的 2 个线程通信;

Q:Handler 为什么需要使用底层的 epoll 来休眠?

  1. 需要兼顾 Native 层的消息,消息可能来自底层硬件;

  2. 如果只考虑 Java 层,notify/wait 即可实现;


因篇幅的原因,我在本文里只选取了部分问题,完整的 Handler 相关的 40+ 个问题,已整理成 PDF,可在公众号后台回复 「Handler问答」获取,也可以直接点击底部「阅读原文」跳转到幕布分享链接。

另外你还遇到过什么 Handler 相关的问题,欢迎在留言区留言讨论。

本文对你有帮助吗?留言、转发、点好看是最大的支持,谢谢!

公众号后台回复成长『成长』,将会得到我准备的学习资料。

面试常客「Handler」的 40+ 个高频问题 Q A 对答!相关推荐

  1. 2017Android面试回忆录「下」(今日头条/小米/网易/知乎...)

    前言 面试合集 之 滴滴.美团.腾讯.阿里.头条.小米.网易- PS: 时间周期:[2017/6 – 2017/7] 来源: [本部分由「洛廷」和「剑胆诗魂」提供] 没有严格按照面试轮次来区分 今日头 ...

  2. 跨境电商「独角兽」融资40亿+,这家公司是怎么做增长的?

    PatPat 作为目前是全球增速最快,童装品类最多的 DTC 品牌,诞生到现在,7 年时间实现快速增长,多次上过 App store 主页推荐,「攻占」了全球 100 多个国家和地区的母婴市场.为什么 ...

  3. 「乾坤」学霸同保送!双胞胎帅哥一起上北大,哥哥本科发表2篇SCI

    视学算法报道   编辑:桃子 [新智元导读]有一种「默契」叫一起上北大.近日,一对双胞胎兄弟双双圆梦北大,哥哥李世乾被保送至北大信息科学技术学院直博,弟弟李世坤被保送至北大工学院.2022年,「乾坤」 ...

  4. 没答好「进程间通信」,被面试官挂了....

    作者 | 小林coding 来源 | 小林coding 前言 (图片来自公众号小林coding) 炎炎夏日,张三骑着单车去面试花了 1 小时,一路上汗流浃背. 结果面试过程只花了 5 分钟就结束了,面 ...

  5. 凉了!张三同学没答好「进程间通信」,被面试官挂了....

    前言 开场小故事 炎炎夏日,张三骑着单车去面试花了 1 小时,一路上汗流浃背. 结果面试过程只花了 5 分钟就结束了,面完的时候,天还是依然是亮的,还得在烈日下奔波 1 小时回去. 面试五分钟,骑车两 ...

  6. 张三同学没答好「进程间通信」,被面试官挂了....

    前言 开场小故事 炎炎夏日,张三骑着单车去面试花了 1 小时,一路上汗流浃背. 结果面试过程只花了 5 分钟就结束了,面完的时候,天还是依然是亮的,还得在烈日下奔波 1 小时回去. 面试五分钟,骑车两 ...

  7. 记一次「失败」的 Redis 面试

    前两天,一哥们去面 Go 后端开发,起初 SQL 优化技巧.分布式架构.中间件都答得都挺好,没想到最后折在了 Redis 上,回来跟我复盘了一波: 面试官:"Redis 什么时候做 Reha ...

  8. 几道 BAT 算法面试中经常问的「字符串」问题

    https://www.toutiao.com/a6675839856192520711/ String 作为最常见的编程语言类型之一,在算法面试中出现的频率极高. 1. 验证回文串 题目来源于 Le ...

  9. 教你如何在面试中用「10分钟快速分析」一款产品

    本文为PMCAFF专栏作者PMnote出品 通常对产品新人或刚转做产品经理的人来说,大多在面对「产品本身的观察与分析」时都是在面试的时候,为了考察面试者的「产品感」面试官大多都会问「你有用过我们的产品 ...

最新文章

  1. 怎么把dll库写成MATLAB接口,如何在Matlab中应用动态连接库接口技术
  2. php重构ifelse,php - 重构条件语句PHP - SO中文参考 - www.soinside.com
  3. 【学术相关】中国霸榜AI顶会,但引用量最低!最新斯坦福AI指数出炉!
  4. 域名解析文件hosts文件是什么?如何修改hosts文件?
  5. django-模态框添加学生
  6. ‘gbk‘ codec can‘t decode byte 0xb9 in position 58: illegal multibyte sequence
  7. Prefer rather than 喜欢 Prefer to
  8. 2020年的工程咨询将如何影响建设工程?
  9. 史上最全 ArcGIS 软件安装包分享
  10. URP渲染管线实战教程系列 之URP渲染管线实战解密(一)
  11. 第6周作业3-Fibonacci数列(网络131黄宇倩)
  12. 路由器配置深入浅出—路由器接口PPP协议封装及PAP和CHAP验证配置
  13. 我看三十而立的80后
  14. word2vec理解及pytorch实现
  15. 【OpenGL】绘制一个点
  16. Linux系统命令行执行MySQL脚本
  17. 时域同步平均(TSA)降噪原理
  18. 简谱打谱软件音乐梦想家与作曲大师有什么不同
  19. 一些中文自然语言处理工具包
  20. 设计心理学之席克定律和面部辨识

热门文章

  1. java构造单例线程池_java中常见的六种线程池详解
  2. 在wps文档中的表格某位置上加入斜杠
  3. 转 中兴H608b破解 中兴H608b路由 中兴H608b升级 zxv10 H608b破解 zxv10 H608b路由 zxv10 H608b升级
  4. Swift:NSLocalizedString 国际化语言配置
  5. Windows 10 自动登录
  6. 电脑上自己的账户被删了,使用之前的密码无法登录
  7. 用MAPI操作短信邮件
  8. 为什么机器人学不会装配技术
  9. 微信就能查社保、开个税证明啦
  10. 德国SycoTec高速主轴电机在模具切削加工中的应用