看下面例子:

在子线程发送一个消息,然后在主线程街道这个消息处理,这个消息是如何从子线程切换到主线程的呢?首先跟踪一下handler.sendMessage(new Message())如下:


从上面两图看辗转来到sendMessageAtTime方法,看到A处,此处mQueue是何方神圣呢???看名字只是知道个大概是个消息队列, 而B处看方法名都知道是将msg入队列了:
mQueue在源码中两处被赋值,都是在构造方法里:


本文这个例子创建Handler对象的时候调用的是其无参的构造方法,如下:

正是上上图C处所在的构造函数,从C、D处可以看到Handler对象的MessageQueue是looper对象的成员mQueue赋予的,而looper对象又是如何创建的呢?

嗯,这里我简单的解释一下ThreadLocall,表示每一个线程都有自己的一份备份,相互独立。而主线程是什么时候设置这个ThreadLocal的呢??其实在我之前的文章Activity生命周期回调是如何被回调的?中源码分析的时候已经有涉及到这一点:

如上图E1处:

F处又跳转调用了prepare方法:

这里注意一下咯:Looper构造函数是私有的,在类外部是没办法直接new设置给一个线程的,不过也不用担心,调用他的prepare这个静态方法就可以了,所以这里记 住哦,Looper的prepare方法很重要,可以给一个线程“挂载”一个Looper对象,好啦回到F处,接下来看下标记G处呗:

接下来看到上图中的E2处调用了Looper.loop方法:

从上面两张图可以看到,Looper.loop方法就是进入一个死循环,在循环体内不断地从队列取出消息,然后进行分发处理见标记E3处,嗯,上图你可以看到有一个E4,这里计算了消息分发耗时,其实这里有一个应用,可以利用这里点计算UI线程是否卡顿,像这个问题的文章已经是烂大街了,随便百度一下都有,嗯,有时间我可能也会写一篇总结一下,回到上面提到的标记E3处:
msg.target.dispatchMessage(msg);我们知道这个msg是Message对象,而这个target呢??它其实是一个Handler对象,问题是这个target又是什么时候被赋值的??好吧,你暂且先记下这个疑问待会再说咯:

好的,到目前为止你已经知道消息是如何派发哪里被处理的了,继续往回翻,回到标记C处:


所以C处只是取出当前线程“挂载”的Looper对象而已,接下去看D,“mLooper.mQueue”这分明是说mQueue是mLooper的成员呀,进去Looper内部研究研究成员结构:

所以到这里你已经了解以下这些事实:

  • Looper对象可以通过静态方法prepare“挂载”一个线程上
  • Handler对象内部用到的Looper、MessageQueue都是来自当前线程“挂载”的Looper(顺便提醒一下,这个事实就说明了一个线程要使用Handler之前要先让这个线程“挂载”上一个Looper对象,哈哈,那又怎么让线程“挂载”上一个Looper呢?上文不是说了吗——调用prepare静态函数)
  • Looper的loop方法会启动一个死循环不断的从队列中读取消息,然后将消息送到Handler对象的handleMessage方法中处理

嗯,现在可以回到文章开头的标记B处,这里可以看到当调用Handler对象来发送消息时,会调用enqueueMessage方法,将消息入队列:

见上图,标记 I 处是真正的将消息入队列,不过在入队列之前多做了一项操作见标记H处——将handler对象设置到msg消息对象的target成员中,到此上文那个叫你暂时存档的疑问也帮你解决了。

按国际惯例来个总结咯:

  • 一个线程在使用Handler之前要先在线程上挂载一个Looper,android主线程在ActivityThread的main方法已经做了这个挂载步骤,所以平时我们在主线程之间创建Handler对象式直接可以使用的不会出错,但是在子线程创建Handler对象就必须先帮线程挂在一个Looper对象。

  • Looper的loop方法会开启一个死循环不断读取消息队列的消息,然后传给handler对象的handleMessage方法处理

  • handler对象在发送消息的时候,会将消息入消息队列,压入队列之前会将自身引用设置在Message消息对象的target成员方便loop死循环读取消息之后分发消息。

源码分析Handler机制相关推荐

  1. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

  2. Dubbo 源码分析 - SPI 机制

    1.简介 SPI 全称为 Service Provider Interface,是一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类.这样可以 ...

  3. JQuery源码分析 - 闭包机制在jQuery中的使用及冲突解决

    jQuery中的闭包机制 本系列中我们将基于jquery3.5.1版本对jQuery源码进行分析,分析以源码加注释的方式展示. 本节中将分析jQuery源码中的 14 ~ 40行:自执行函数定义.环境 ...

  4. SOFA 源码分析 — 扩展机制

    前言 我们在之前的文章中已经稍微了解过 SOFA 的扩展机制,我们也说过,一个好的框架,必然是易于扩展的.那么 SOFA 具体是怎么实现的呢? 一起来看看. 如何使用? 看官方的 demo: 1.定义 ...

  5. Android-33源码分析: Handler消息机制

    的声明: ActivityThread 管理应用进程中主线程的执行,根据AMS调度执行广播和其他操作 Handler 发送消息并处理消息 MessageQueue 用于存放消息的消息队列 Looper ...

  6. 【转】从源码分析Handler的postDelayed为什么可以延时?

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/aliankg/article/details/70842494 Thread/Hander/Loop ...

  7. Struts2 源码分析——拦截器的机制

    本章简言 上一章讲到关于action代理类的工作.即是如何去找对应的action配置信息,并执行action类的实例.而这一章笔者将讲到在执行action需要用到的拦截器.为什么要讲拦截器呢?可以这样 ...

  8. MyBatis 源码分析 - SQL 的执行过程

    本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析.运 ...

  9. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

最新文章

  1. JavaFX 8的弹出式编辑器
  2. Arduino 各种模块篇 DHT11 温度湿度 数字模块 单总线
  3. python locust 性能测试:HOOKS钩子方法
  4. RSLogix 5000下载程序方法
  5. 2019-2025新能源汽车发展趋势与产业链技术路线解析
  6. 关闭安卓系统导航栏右下角自动旋转按钮
  7. iOS:iOS 的 APP 如何适应 iPhone 5s/6/6Plus 三种屏幕的尺寸?
  8. 做独一无二的自己,颜宁西湖大学问答全记录
  9. Mariadb 设置远程访问
  10. 内存(DDR/DDR2/DDR3/DDR4)的速度等级和时钟频率———个人笔记
  11. BGA集成电路脚位识别
  12. 使用LVS构建可伸缩WEB集群
  13. python牛顿迭代法求根例题_python求根算法
  14. 数据结构 —— 队列、栈、链表的区别
  15. 分段存储管理+逻辑地址转化为物理地址+例题
  16. 基于visual Studio2013解决面试题之1003字符串逆序
  17. 金蝶ERP供应链之采购管理
  18. 在这个内卷的时代,你不要怪内卷
  19. 合肥内推 | 商汤智能车舱研发团队招聘计算机视觉算法实习生
  20. 计算机学科建设目标 措施,建设目标、定位与措施-虚拟仿真实验室

热门文章

  1. 6.QML动画——状态与过渡
  2. 21天mysql_把整个Mysql拆分成21天,轻松掌握,搞定(下)
  3. 区分JavaScript中slice与splice方法
  4. ioS html的转义
  5. SQL Server 2008 R2 安全性专题(一):安全原则
  6. OCCI入门(VC2010下配置)
  7. 内存Cookie和硬盘Cookie
  8. Flutter ------- WebView加载网页
  9. php代码审计之MetInfo5.3盲注
  10. 浅谈format格式化输出