文章目录
一、消息机制原理
Handler消息机制老生常谈了,必备八股之一。但是每次看都有新收获,故好好总结一下Handler相关知识。
1.1 基本概念
1、Handler
用于发送和处理消息的类,有多种重载的构造方法,通过一系列sendXXX
和postXXX
方法来发送消息到消息队列,然后通过实现Handler.Callback
接口或重写handleMessage
方法处理消息
2、MessageQueue
消息队列,它是一个链表结构,用以存放handler发送的消息,实现了获取消息的方法next()
和移除消息及消息处理回调的方法(removeXXX
系列方法)
3、Message
消息,承载一些基本数据,消息队列存放对象。维护了一个消息对象池,可以复用消息,避免创建太多消息对象占用过多内存,导致APP卡顿。
消息分类:
4、Looper
消息机制的灵魂,用以不断调度消息对象并且分发给handler处理。Looper是同线程绑定的,不同线程的Looper不一样,通过ThreadLocal实现线程隔离。
1.2 消息机制主流程
1、发送消息
可以使用sendMessage(以及一系列 sendXXX的消息发送方法)和post方法发送即时同步消息,或通过sendXXXDelayed和postDelayed发送延迟同步消息。
如果是通过sendXXX方法发送即时或延时消息,最终都会辗转调用到sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
方法,然后调用enqueueMessage
方法。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;// ① 设置处理该消息的handler对象
msg.workSourceUid = ThreadLocalWorkSource.getUid();
// ② 设置消息类型,同步或异步
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// ③ 交由消息队列的入队方法
return queue.enqueueMessage(msg, uptimeMillis);
}
该方法主要有3个作用,注释中的①②③分别说明了。
2、消息入队
消息入队最终是靠消息队列的恩queueMessage方法完成,其代码如下
boolean enqueueMessage(Message msg, long when) {
// ①
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
// ②
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// ③
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// ④
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// ⑤
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
注释中标明了5个注意点,👇一一说明下:
① 消息对象必须指定target
,也就是处理消息的handler对象;而且message对象的flag
为FLAG_IN_USE
。否则将抛出异常。
②设置消息对象标志FLAG_IN_USE
和时间,创建唤醒字段,用于标记是否需要唤醒消息队列
③如果当前消息队列没有消息或要入队的消息when
值小于对列头消息when
值,则将新消息插入到链表头部。设置needWeak
,它又由mBlocked
变量决定,mBlocked
的设置是在next()
方法中,简单来说消息队列仅有延时消息或空队列时,mBlocked
为true
④不满足③的情况下,从消息链表头开始遍历,将新消息插入到第一个when值大于新消息when值的消息节点前方。
例如当前消息队里:100 - 30 -20 -10(数字表示消息的when值)