文章目录
- 一. 你说一说Handler机制吧
- 二、你知道Handler的同步屏障吗?
- 2.1 Handler消息的分类
- 2.2 什么是同步屏障
- 2.3 为什么要设计同步屏障
- 2.4 同步屏障的用法
- 三、Looper一直在循环,会造成阻塞吗?为什么?
- 扩展阅读
一. 你说一说Handler机制吧
面试官:
我们来开始吧,第一个问题,请你说一说Handler机制吧。
面试者:
好的,Handler机制是Android中用于线程间通信的工具。它主要由四个部分组成:Handler、Message、MessageQueue和Looper。
- Handler:用于发送和处理Message对象。
- Message:表示要传递的信息,可以包含数据。
- MessageQueue:消息队列,存储所有发送的Message对象。
- Looper:用来不断从MessageQueue中取出Message,并交给对应的Handler处理。
-
首先就是
Handler
调用sendXXX
系列或者postXXX
系列的方法发送Message
,不管是哪种方法都是需要从全局消息池里
面拿出一个Message
对象,即obtain
(虽然Message
有个全局消息池,其实它的内部实现只是一个对链头进行插入和删除的单链表
,毕竟单链表在插入和删除上比较有优势), -
接着对该
Message
对象进行各种成员变量的赋值后,把它发送到相应线程的消息队列
中,即enqueueMessage
(虽然MessageQueue
叫消息队列
,但它的内部实现并不是队列
,和全局消息池一样,也是一个对链头进行插入和删除的单链表
), -
之后的工作交给相应线程的
Looper
对象,它通过调用loop()
方法,无限循环地往消息队列里取消息,即next()
, -
如果队列里无消息,它便被阻塞了(
nativePollOnce
和nativeWake
,本地方法使用Linux
的epoll
模型)。 -
如果有消息便把消息取出来,同时消息队列伴随着删除该消息的操作,然后调用
Message
对象的target
成员变量的dispatchMessage
方法进行消息分发处理,target
实际上就是Handler
的实例。 -
Handler
把消息处理完后,Looper
便要把这条消息进行回收再利用,即recycle()
,对消息进行“净身”,即置空消息的所有成员变量,之后保证消息无污染地插入全局消息池中,避免OOM
。
二、你知道Handler的同步屏障吗?
面试官:
很好,你对Handler机制解释得很清楚。接下来第二个问题,你知道Handler的同步屏障吗?
面试者:
是的,我知道Handler的同步屏障。
2.1 Handler消息的分类
首先,Handler发送的消息分为三种:
- 同步消息(普通消息)
- 屏障消息
- 异步消息
通常我们使用handler
发送消息,都是使用默认的构造函数构造handler
,然后使用send
方法发送。这样发送的消息都是普通消息
也就是同步消息
,发出去的消息就会在MessageQueue
中排队,
异步消息
正常情况下跟同步消息
没有区别,只有在设置了同步屏障
之后,才会出现差异。
2.2 什么是同步屏障
开启同步屏障
的第一步需要发送一个特殊消息作为屏障消息
,当消息队列检测到了这种消息后,
就会从这个消息开始,遍历后续的消息只处理其中被标记为“异步”的消息
,
一旦Looper
在处理消息时遇到屏障消息
,那么就不再处理普通消息
,而仅仅处理异步消息
。所以叫“同步屏障”。
相当于给一部分消息开设了"VIP”优先通道。当使用完同步屏障后我们还注意移除屏障。
不再使用屏障
后,需要撤销屏障
,不然就再也执行不到普通消息
了。
2.3 为什么要设计同步屏障
为什么需要这样?它是设计来为了让某些特殊的消息得以更快被执行的机制。
比如绘制界面,这种消息可能会明显的被用户感知到,稍有不慎就会引起卡顿、掉帧之类的,所以需要及时处理(可能消息队列中有大量的消息,如果像平时一样挨个进行处理,那绘制界面这个消息就得等很久,这是不想看到的)。
在请求监听Vsync
信号时,阻塞Handler
消息队列中的同步消息
,优先保证接收Vsync
信号的异步消息
,及时生成新的屏幕数据,供屏幕显示。
我们手机的屏幕刷新频率有不同的类型:60Hz、120Hz 等。
60Hz 表示屏幕在一秒内刷新 60 次,也就是每隔 16.6ms 刷新一次。
屏幕会在我们的手机屏幕刷新频率有不同的类型,60Hz、每次刷新的时候发出一个 VSYNC 信号,通知CPU进行绘制计算。
2.4 同步屏障的用法
屏障消息仅仅是起一个屏障的作用,本身一般不附带其他东西,它需要配合其他Handler组件才能发挥作用。
同步屏障主要用于在某些情况下需要阻止普通消息的处理,而优先处理同步消息。 它通过postSyncBarrier
和removeSyncBarrier
方法来实现。
当调用postSyncBarrier
方法时,会向消息队列中插入一个同步屏障,之后的普通消息会被阻止执行。
只有同步消息(如sendMessageAtFrontOfQueue
发送的消息)才能绕过屏障继续执行。
同步屏障通过返回一个Token
,可以使用这个Token
来移除屏障,恢复普通消息的处理。
例如:
int token = handler.getLooper().getQueue().postSyncBarrier();
// 发送同步消息
handler.sendMessageAtFrontOfQueue(handler.obtainMessage());
// 移除同步屏障
handler.getLooper().getQueue().removeSyncBarrier(token);
这种机制在某些需要严格控制消息顺序的场景下非常有用。
三、Looper一直在循环,会造成阻塞吗?为什么?
面试官:
很好,你对Handler的同步屏障理解得很透彻。最后一个问题,Looper一直在循环,会造成阻塞吗?为什么?
面试者:
Looper本身是一个无限循环,它会不断从MessageQueue中取出消息并处理。如果消息队列为空,Looper会进入阻塞状态,直到有新的消息到来为止。
虽然这是一种阻塞,但它不会消耗CPU资源,因为它内部使用了epoll
或poll
等高效的系统调用来等待新消息的到来。
在MessageQueue.next()
方法里,会调用一个native
方法:nativePollOnce(long ptr, int timeoutMillis)
,当主线程没有消息可处理的时候,该方法会阻塞主线程。具体的阻塞和唤醒机制,就是Linux
的epoll
机制了(Linux 下作为高并发 IO 实现的秘密武器)。
因此,Looper
的这种阻塞是非忙等待
的,不会对系统性能造成负担。只要消息队列中有消息,Looper
就会立即唤醒
并处理消息
,因此不会影响应用的响应速度。
在UI线程中,我们通常会有一个Looper
不停地循环,这样可以确保UI线程能够及时处理用户输入和其他消息,而不会出现卡顿的情况。
面试官:
你的回答非常全面,对Handler机制以及相关概念理解得很深入。非常感谢你来参加面试,有没有什么问题想问我们的?
面试者:
谢谢您的提问和指导。目前我没有特别的问题。
面试官:
好的,那我们今天的面试就到这里,非常感谢你的时间。祝你好运!
面试者:
谢谢您!
以上就是模拟的面试对话场景,希望对你有帮助。如果还有其他问题,可以随时提出。
扩展阅读
- 大概说说Handler机制
- Handler同步屏障
- android 什么是同步屏障 handler同步屏障场景
- 终于搞明白了什么是同步屏障
- 【Android】主线程中的Looper.loop()一直无限循环为什么不会造成ANR?