前言
仅用于知识点简介,详细描述建议百度,主要是一些个人的理解总结,可以在面试时来回答,所以并不是很详细,只是总结~ 后续应该会不断更新
Activity、Service、BroatcastReceiver、ContentProvider
1. Handler 的作用
handler 是Android 线程间的消息机制,主要作用是将一个任务切换到指定的线程(构成handler的looper所在的线程)中去执行。
2. handler 的架构
Handler 的运行需要底层的 message queue(消息队列) 和 looper 做支撑
3. handler 原理
3.1 Message queue 消息队列
单链表数据结构,通过 enqueue message 方法插入消息,通过next 方法取出消息
3.2 looper 消息调度者
- looper 取消息过程
- 如果队列中有消息,判断消息执行时间是否大于当前时间
- 如果大于,就调用 nativePollOnce阻塞一段时间,然后取出消息执行
- 否则直接执行
- 没有消息,一直阻塞,直到下一个消息到来,唤醒线程继续循环
- 如果队列中有消息,判断消息执行时间是否大于当前时间
- Looper 方法解析
- 主线程 - ActivityThread 的main 方法,就是App进程入口,通过Looper.prepareMainLooper 在主线程中存储一个 looper
- 在子线程中,我们需要通过 Looper.prepare 在子线程存储一个 looper,再通过 looper.loop 开启一个消息循环
3.3 handler
通过 send 发送消息,实际是忘 looper 的 messagequeue 插入一条消息:
改变 消息的 target 为发他的 handler -> looper 取出消息 -> dispatchmessage 处理消息
4. 主线程的 handler
- 主线程中的所有操作都是通过主线程中的Handler处理的,包括Activity的生命周期等
- ActivityThread 的 main 方法是 App 程序的入口,在 main 中使用 Looper.prepareMainLooper() 设置 looper,然后创建 ApplicationThread 与 server 的 ApplicationThreadProxy 进行进程通信,然后调用 Looper.loop() 开启消息循环
- 生命周期:由系统服务 ActivityManagerService 调用 ApplicationThreadProxy 通过 Binder 给当前 app 进程中的 ApplicationThread 发送 暂停 Activity 或 start create 等动作时,app 进程中的 ApplicationThread 便会在 主线程 的 handler 将这个动作消息插入到主线程的 MessageQueue 中去处理
5. Looper 的死循环对性能的消耗问题
looper 区消息的机制就涉及到 Linux pipe/poll 机制,在 MessageQueue 没有消息时,主线程边阻塞在 loop 的 queue.next 中的 nativePollOnce() 方法,此时主线程变回释放CPU来进入休眠状态,知道下个消息到达时,触发 nativeWake,通过pipe管道唤醒主线程继续工作。所以不会消耗cpu资源
6. Looper.loop() 中的死循环 和 ANR 的区别
- Looper.loop() 死循环存在的意义在于循环处理队列的消息,否则在取出一条消息处理后,主线程就退出,而死循环可以让主线程等待下一条消息继续处理,且等待时并不会造成性能的消耗
- ANR 的原因则是message Queue 中的一个消息处理时间过程,导致接下来的消息无法处理,用户的操作等无法及时响应,才会出现ANR
7. handler.postDelayed 方法怎么实现的?
经 postDelayed 方法加入到 messageQueue,在加入前会计算出这个消息实际应该执行的时间 (SystemClock.uptimeMillis() + delayMillis,其中SystemClock.uptimeMillis()是开机到现在的时间,不会随用户修改系统时间而改变,然后通过 EnqueueMessage 将message 和执行时间一并添加到 messageQueue,EnqueueMessage 会根据执行时间将 Message 插入到适当的位置,也就是说 MessageQueue 是按照 message.when (执行时间)排序的
8. handler.postDelayed函数延时执行计时是否准确
当上一个消息存在耗时任务的时候,会占用延时任务执行的时机,此时是不准确的。那么如何准确执行延时任务呢,可以开启一个HandlerThread为一个专门的唯一的延时消息服务。
9. 系统是如何保证延时消息精确执行
- 从 Looper 取消息的方式来看
- 如果当前没有消息,那么会插入到队首,在时间到达 message.when 时,就会开始执行消息
- 如果当前有消息,那么也会通过死循环,在时间到达 message.when 时,取出消息执行
- 从加入一条新的消息来看
- postDelayed 会计算实际执行时间,EnqueueMessage 会将message 根据 实际执行时间插入到适当的位置
- 而这两个动作都是没有延迟的,所以可以保证消息的精确执行
10. ThreadLocal
ThreadLocal 可以在多个线程内存储数据,使用ThreadLocal 存储的数据在多个线程间是隔离的,因为它是将数据存储在每个线程内的 ThreadLocalMap
11. 同步屏障、同步消息、异步消息
- 同步消息:就是我们平常发送的消息
- 异步消息:一般是系统发送的消息
- 同步屏障:就是给消息队列发送了一个屏障信息,消息队列在处理到这个屏障信息时就开启了“同步屏障”模式
- 在该模式下,只返回异步消息给Looper处理,屏蔽同步消息。
- 在处理异步消息队列后,计时消息队列还有同步消息也会通过nativePollOnce() 进入线程阻塞,直到有新的异步消息进入 或 解除同步屏障模式,同步消息才能得到处理。
- 主要是告知系统优先处理此消息,在系统的 invalid 等UI刷新的方法中,就可以看到 MEssage 设置 为 异步消息,来保证优先执行最高优先级