
Android - Handler原理

Handler的主要作用是收发消息和切线程
功能一:收发消息
简单流程介绍
希望你看完这篇文章后也可以把流程自己讲出来,并且每个环节还可以讲出很多细节
他的消息机制离不开Looper、MessageQueue
- 其中
Looper
每个线程只能持有一个,主要负责循环查看MessageQueue
里面是否有msg
需要处理,并将需要处理的消息取出,交给Handler
MessageQueue
是负责存放消息的,数据结构是一个单链表,这样就可以方便地插入或删除msg
具体流程一般是:
Handler
发送一条msg
=> 本质是向MessageQueue
里插入一条msg
,插入时候的依据是msg.when
=>SystemClock.uptimeMillis() + delayMillis
这条
msg
被MessageQueue.next()
返回并交给Handler
去处理,next()
会在有同步屏障(msg.target==null
)的时候 遍历查找并返回最早的异步消息
,并在移除屏障后,从头取出并返回 同步消息
ps: 同步屏障就是在消息队列中插入一个屏障(target = null
的消息),插入之后,所有的同步消息都会被屏蔽,不能被执行,但是异步消息却不受影响,可以继续执行。
Handler.dispatchMessage(msg)
会优先处理msg.callback
,如果msg.callback
为空,就处理Handler.mCallback
,然后处理是msg
本身。
msg.callback
是在调用Handler.post(Runnable)
时,里面的Runnable
runOnUIThread
,view.post(Runnable)
也用的是Handler.post(Runnable)
,Runnable
是一样的)
这是在不新增Handler
的情况下,另一种调用Handler
的方式(如下)
1 | class MyHandlerCallBack: Handler.Callback { |
可以看到他也有handleMessage
这个方法
Looper是个死循环
(1)死循环的目的
目的就是让主线程一直卡在这个死循环里面
因为Looper的作用就是在这个死循环里面取出消息,然后交给Handler
处理
Android的生命周期,你了解的onCreate,onStop,onStart…… 等等都是由Handler
来处理的,都是在这个死循环里面运行的
所以什么Looper
死循环卡死主线程怎么办???
必须给我卡住!!!不卡住的话,消息就没法整了!!!
看下Android启动的时候的源码
Activitythread.java >> main()
1 | public static void main(String[] args) { |
想想写java的时候,main最后一行执行完了,不就彻底玩完了嘛!!!
(2)死循环里干了啥
其实想都不用想,一直在看MessageQueue
里面有没有消息呗,太简单了!
咋看?
答: 调用MessageQueue.next()
看下源码 Looper.java >> loop()
1 | for (;;) { |
很简单,next()
返回Message
,msg.target.dispatchMessage()
处理Message
但是队列里没消息就会返回null,这是错误的!!!具体往下看
MessageQueue是个单链表
1.插队
Handler
发消息的时候,目的就是对msg
经过一系列操作,最终也只是调用enqueueMessage
插入队列而已
看下源码 Handler>>enqueueMessage()
1 | private boolean enqueueMessage(long uptimeMillis) { MessageQueue queue, Message msg, |
return
直接调用Message的插入队列方法
2.出队
出队就是next()
方法,之前已经见过了
(1)时间顺序
Message
是按时间排序的,也就是msg.when
=> SystemClock.uptimeMillis() + delayMillis
msg.when
是Message
期望被处理的时间
SystemClock.uptimeMillis()
是开机到现在的时间,delayMills
是延迟时间,这个在sendMessageDelayed
方法里直接可以直接传参
next()
就是按照时间顺序处理MessageQueue
里面的消息的
但是next()
里有个概念叫 同步屏障
(2)同步屏障
同步屏障,就是说,平时MessageQueue
都是处理同步消息,也就是按顺序来,一个个出队
同步屏障就是阻挡同步消息的意思
就是msg.target == null
的时候,MessageQueue
就会去找msg.isAsynchronous()
返回true
的msg
isAsynchronous,没错 ! 这是异步消息,就是优先级很高,需要立刻执行的消息,比如:更新View
ps:同步屏障不会自动移除,使用完成之后需要手动移除,不然会造成同步消息无法处理。
通过removeSyncBarrier(int token)方法进行移除,token就是之前添加屏障时返回的token。
(3)阻塞
值得注意的是,讲Looper的时候,源码next()
后面官方给我们注释了 // might block
可能阻塞,也就是说可能这个next()
也许会执行好久
next()
会阻塞?,什么时候阻塞?
now < msg.when
也就是时间还没到,期望时间大于现在的时间
(4)退出
另外看第一行,只有ptr == 0
,才会返回null
所以上面才说next()
不会因为没消息而返回null
,原来返回null
的时候在这呢!
看下源码,MessageQueue.java >> next()
1 |
|
代码简略了还是有点多,别着急,慢慢看
那pre
什么时候就是0
了呢?
答:quit()
了之后
看下源码,Looper.java
1 | public void quit() { |
可以看到只是一个传参不同而已,下面看看这个参数是干嘛的
看下源码,MessageQueue.java >> quit()
1 | void quit(boolean safe) { |
可以看到,safe == true
,就移除未来的Message
safe == false
,就移除所有的Message
mQuiting
变成了true
,记住他我们一会儿会用到
而改变ptr的地方在这里
在next()
里面
里面有个dispose
,找不到可以ctrl+F
找一下
这里只有在mQuiting == true
的时候,才会调用
这就是改mPtr
的地方,然后下次next()
的时候就会返回null
了
Handler流程
(1)post过来的msg
我们已经知道了在Looper
的死循环里面,会将next()
返回的msg
交给Handler
,调用dispatchMessage()
dispatchMessage()
里面会先判断msg
是不是被post
过来的,因为post
要执行的逻辑在msg.callback
里面,callback
是一个Runnable
,这可能不是很好理解
你可以想想runOnUIThread(Runnable)
,这里的Runnable
就是上面的callback
,
他们都是调用了Handler.post(Runnable)
至于为啥起个名叫callback
,我也纳闷儿
(2)send过来的msg
这些msg
是会的逻辑是你重写的handleMessage
那里的逻辑
如果实现了Handler.Callback
这个Interface
,就会处理mCallback
的handleMessage
而不是Handler
自己的handleMessage
这是一个优先级策略,没什么好奇怪的
我们看下源码 => Handler.java >> dispatchMessage()
1 | public void dispatchMessage( { Message msg) |
这就是Handler的消息机制了
接下来我们讲讲Handler
的另一个功能,切线程
功能二:切线程
Handler切线程使用的是ThreadLocal
(1)ThreadLocal
ThreadLocal
是线程里面的一个数据储存类,用法类似map
,key
就是thread
但是他没有提供,根据key
来找ThreadLocal
的Values
的方法,所以暴露的api
就只能让你去get
当前线程的ThreadLocal
的Values
对象而已,就是key
——你自己没法作为参数传进去,只能是currentThread
如果你没用过ThreadLocal
,我给你举个例子
1 | fun main() { |
结果是这样的:你可以自己运行看看
1 | in Thread[main] booleanThreadLocal value = true |
(2)切线程的细节
话说回来,Handler怎么通过ThreadLocal切线程的呢?
答案是:Looper
是放在ThreadLocal
里的
回顾片头的流程,Handler
将消息插入MessageQueue
,然后Looper
取出来,再还给Handler
,这种设计不止是为了让msg
可以按顺序处理,还可以让外部接口只有Handler
最关键的是,Looper
跟Handler
的触发关系只有Looper
触发Handler
,Handler
不会触发Looper
因此Handler
把消息放在MessageQueue
之后,就在等着Looper
来给自己派发任务(msg
)
举个例子:
线程A调用主线程的Handler
发一个消息
Handler
将这个消息插入MessageQueue
,此时其实还在线程A里
只有Looper
在next()
调用msg.target.dispatchMessage()
时,就变成了主线程了
仅仅是因为Looper
在 主线程 而已
OVER
- Title: Android - Handler原理
- Author: lucas
- Created at : 2025-05-21 16:59:41
- Updated at : 2025-05-21 17:02:57
- Link: https://darkflamemasterdev.github.io/2025/05/21/Android-Handler原理/
- License: This work is licensed under CC BY-NC-SA 4.0.