为什么使用Handler机制?
Android应用位于一个进程中运行,应用启动时 Android 系统会分配一个 Jvm 进程来执行,同时进程中会启动一个主线程 MainThread(也称作为 UI 线程),负责处理与 UI 相关的事件,比如组件的渲染、用户与屏幕之间交互事件并将事件分发到对应的组件进行处理等;
Android 开发中只能通过主线程来更新UI(谷歌进行了开发约束):
因为UI操作并不是线程安全的,如果多线程进行UI操作,会导致结果不可预测,所以要在同一个线程即主线程进行UI操作;
UI操作也不能加锁,加锁会增加性能的下降,无法及时更新渲染UI组件,同时也会增加UI操作的复杂度;
对于一些耗时操作,不能在主线程中进行,因为主线程一旦执行耗时操作,那么表现在屏幕上就是卡顿甚至无响应,因此需要使用子线程来完成这些操作;
耗时操作只能放在子线程,但是子线程无法更新UI,Handle 就是用于解决子线程更新UI的矛盾,更准确来说可以用来进行线程之间的通信,使得子线程可以向主线程发出更新UI的通信,进而主线程接收到这些信息后进行UI的更新;
Handler架构

四大部分:
Message:消息实体,用于封装消息的一些信息,比如执行时间、执行代码等;MessageQueue:消息队列,内部是基于单链表实现的 以message执行时间升序 排序的优先队列(并没有继承 PriorityQueue)用于存放供 Handler 处理的消息;
通过
enqueueMessage将消息入队,通过next将消息出队;
Looper:类似一个泵,不断的从MessageQueue中查看是否有消息并将其出队,交由Handler处理;Handler:可以发送消息实际上都会调用
MessageQueue#enqueueMessage方法将消息发送;通过
Looper取出消息队列中的消息交由Handler#dispatchMessage进行分发,最后到Handler#handleMessage进行处理,也就是自己实现 Handler 时通常会重写的方法,用于处理接收到的消息;
代码调用链
源码分析
Handler发送消息
Handler中发送消息的函数有多个:
post:发送一个立即执行的RunnablepostDelayed:发送一个延迟执行的RunnablepostAtTime:发送一个指定时间执行的RunnablepostAtFrontOfQueue:发送一个Runnable到队首,下一次迭代时执行;sendMessage:发送一个 Message...
以 post 为例:首先封装成一个 Message,并调用 sendMessageDelayed
// Handler#post
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}检查参数,并调用 sendMessageAtTime 指定在某个时间点(当前时间+延迟时间)执行
// Handler#sendMessageDelayed
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}检查当前消息队列是否存在,准备入队:
// Handler#sendMessageAtTime
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}将发送的消息的执行体设置为当前 Handler实例,将其入队消息队列:
// Handler#enqueueMessage
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}MessageQueue入队&出队
Handler#enqueueMessage 最终调用的是 MessageQueue#enqueueMessage
该方法就是将 传递的 msg 参数插入到内部维护的单链表,按照 when 升序排列;
// MessageQueue#enqueueMessage
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.when = when;
// 头部节点(单链表)
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// 将当前 msg 插入为队首
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
...
Message prev;
// 在链表中查找到合适的位置(按照when升序排列)插入
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;
}
...
}
}既然有入队操作,那么就会有出队操作:
next 用一个死循环轮询单链表的头部是否为空:
如果不为空则表示有消息,则判断消息的执行时间
when是否已经到了如果到了则返回该消息
否则等待指定时间
如果为空则堵塞等待有消息到来;
// MessageQueue#next
Message next() {
...
// 死循环,直到消息队列中有消息出现才退出
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 底层实现,堵塞指定时间
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
...
if (msg != null) {
if (now < msg.when) {
// 当前时间小于消息执行的时间,要等待
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// now >= msg.when: 消息可以执行了
mBlocked = false;
// 从队列中取出消息
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
...
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}
...
}
}到这里可能会有疑问,假设已经有一个消息被拿出来,但是距离执行的时间还有很久,消息队列在这段时间会堵塞线程,如果在此期间有新消息入队,那岂不是没办法处理这个新的消息?
在
enqueueMessage中有一个needWake字段,表示是否需要唤醒当前堵塞的线程,也就是调用nativeWake方法当有新的消息入队且当前消息队列正在堵塞,才会调用
nativeWake唤醒当前线程;这样就保证了在堵塞时间内能够及时处理新入队的消息;
Looper取出消息
loop 方法内部有一个死循环,只有 looperOnce 返回 false才会终止,但是只有消息队列关闭的时候才会返回 false,所以这里的 loop 会一直循环;
// Looper#loop
public static void loop() {
// 拿到当前线程的 looper
final Looper me = myLooper();
...
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}loopOnce 负责从消息队列取出消息,如果消息队列中没有消息,则会堵塞(详看 MessageQueue#next());
取出消息后将会调用其 target 也就是发送消息的 Handler 的 dispatchMessage()
// Looper#loopOnce
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
// 取出消息队列中的队首消息,如果没有则会堵塞
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// logging relatived
...
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 将取出的消息分发到指定的 Handler
msg.target.dispatchMessage(msg);
...
} catch (Exception exception) {
...
}
...
msg.recycleUnchecked();
return true;
}Handler分发消息
dispatchMessage 将消息 msg 分发各处:
如果消息指定了
callback则调用handleCallback如果 Handler 在创建时传入了
Callback(接口,内部有一个handleMessage方法),则交由该处理;如果上面都没有指定,则交由 Handler 内部的
handleMessage处理(默认是空实现,需要重写)
// Handler#dispatchMessage
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}到这里,Handler 整体的发送消息、分发消息的流程就结束了
问题
为什么不将消息给Handler直接处理,而是需要消息队列这些操作?
Handler 是用于线程之间通信的;
在不同的线程中拿到的 Handler 实例是相同的,但是 Looper 是不同的,每个线程都至多只能有一个 Looper;
轮询以及分发的操作由 Looper 完成
也就是说分发执行的操作是在 Looper 所在的线程进行的;
如果直接给 Handler 处理,那执行消息的线程也就是当前线程,而不是我们期望的线程执行;
因此需要发送给消息队列,由轮询的 Looper 负责调度这些消息,并在 Looper 所在的线程中执行;
ThreadLocal在Handler的作用
首先要知道,线程是共用同一块内存区域,因此创建的对象、修改对象都是同一个对象;
而 ThreadLocal 用于在不同的线程中创建不同的副本;
ThreadLocal是一个线程内部的数据存储类,在指定的线程中存储数据,而存储数据后只有指定线程中可以获取到存储的数据,对于其他的线程就无法获取到数据;
每个 Thread 都有自己的数据副本
Thread.thradLocals,而ThreadLocal就是在对应线程的数据副本写入数据(以ThreadLocal为键,set 的数据为值)
// ThreadLocal
// 拿到对应线程的 ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// 创建对应线程的 ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// 写入
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}在 Handler 机制中,每个线程中都有一个 Looper对象,这个 Looper 对象就是用 ThreadLocal 实现不同线程有不同的副本:
// Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}Looper、Handler、MessageQueue的引用关系?
一个线程中至多有一个 Looper(主线程中的 Looper 会自己创建,而其他的线程需要使用 Looper.prepare 创建)
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}而一个 Looper 在创建时同时也会创建对应的 MessagQueue,也就是说每个 Looper 都有自己对应的消息队列;
// Looper 的构造函数
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}一个线程中可以有多个 Handler 存在,但消息发送和轮询都是由该线程的 Looper 和 MessageQueue 完成;
由
Message.target来区分不同的 Handler
在主线程中创建了 Handler 对象后 (调用无参构造方法,已废弃,仅说明),将会把当前线程即主线程与 Handler 关联;
主线程在创建的时候就自动创建了 Looper 对象以及 MessageQueue;
子线程拿到了主线程中 Hander 对象的引用后,发送的消息都会进入对应 Handler 的消息队列中,也就是主线程的消息队列;
由主线程的 Looper 取出消息并分发给 Handler 执行;
MessageQueue如何保证线程安全?
多个子线程向主线程的 Handler 发送消息,那 MessageQueue如何保证线程安全?
在 MessageQueue#enqueueMessage 方法中,有一个同步块:
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
// insert msg into queue
}
}保证多个线程同时调用该方法时,只有一个线程能够获取到 MessageQueue 对象,只有当该线程完成插入操作后,其他线程才有机会获取到 MessageQueue 对象;
Handler如何导致内存泄漏问题?
Java知识:非静态内部类 & 匿名内部类都默认持有外部类的引用
如果是使用内部类(包括匿名类)来创建 Handler 对象,那么该内部类会隐式持有一个外部类的引用(在开发中通常是一个 Activity);

IDE代码中也会提示内存泄漏
在 Activity 退出后,而消息队列中还有没有处理完的消息:
Message.target持有对应 Handler 引用,而 Handler 又隐式持有外部类 (Activity)的引用;这种引用关系将会一直保持,直至消息队列中的所有消息被处理完毕;
如果在未处理完毕时需要进行销毁外部类,就会导致内存无法回收而内存泄漏
如何解决?
避免使用匿名内部类,改用
Handler(Looper, Callback)构造方法来处理,而不是使用匿名内部类重写Handler#handleMessage方法private val mHandler = Handler(Looper.getMainLooper()) { msg -> // handle message true }在 Activity 销毁的时候,将 Handler 中所有消息都清空(这样可能会导致某些消息没有得到正确处理)
private val mHandler = object: Handler() { override fun handleMessage(msg: Message) { // handle... } } override fun onDestroy() { super.onDestroy() mHandler.removeCallbacksAndMessages(null) }使用 静态内部类 + 弱引用 的方式来处理(这种方法要继承 Handler)
class MainActivity : AppCompatActivity() { companion object { private class MainHandler(activity: Activity) : Handler() { private val activity: WeakReference<Activity> = WeakReference(activity) override fun handleMessage(msg: Message) { super.handleMessage(msg) } } } private val mHandler = MainHandler(this) }
总的来说,还是建议使用第一种方式,因为后面两种方法所使用的
Handler()无参构造方法已经标记为废弃;
子线程创建Handler需要注意的事情
Handler需要一个 Looper,但是子线程中并不会像主线程一样会自动创建 Looper
因此在创建Handler时需要先调用 Looper.prepare() 创建 Looper
Handler 创建时会检查 Looper 是否为空
mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); }
然后需要调用 Looper.loop() 对消息队列进行轮询(如果没有调用该方法,则发送的消息不会被处理)
Looper.loop() 这个方法会堵塞当前线程,换句话说,当执行了这个方法,后面的代码都无法执行;
当子线程的消息队列为空时,Looper一直堵塞线程会浪费资源,因此需要结束Looper的死循环;
Looper.quit() 和 Looper.quitSafely() 支持将退出循环,两者之间的区别就是
前者不管是否已经执行,直接将所有的消息清空
后者会将已经到了执行时间的消息保留并执行,将未执行的消息清空;
// MessageQueue#quit
// Looper#quit实际上调用的就是这个方法
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
// 移除未执行的消息
removeAllFutureMessagesLocked();
} else {
// 移除所有消息,不管是否要执行
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}总结来说,子线程创建Handler需要完成三个步骤:
设置
Looper.prepare()调用
Looper.loop()结束
Looper.quit()或Looper.quitSafely()
主线程中的Looper死循环为什么不会导致应用卡死?
主线程中所有执行的代码都会封装为消息,进入消息队列由 Looper 处理的,Looper 死循环外并没有处理其他事情,因此不会导致应用卡死;
首先了解下应用的启动流程:Launcher进程(桌面点击应用图标)-> AMS(请求启动主Activity)-> Zygote进程(创建并启动应用进程)-> 启动 ActivityThread#main (也就是主线程)
ActivityThread的main方法就是应用的入口:
可以看到内部启动了一个线程,创建了一个 Looper与主线程关联;
随后就是开启 Looper 的死循环,处理所有消息;
如果Looper退出循环还会抛出一个异常,说明这个Looper的死循环的持续时间与应用的生命周期相同;
// ActivityThread#main
public static void main(String[] args) {
...
// 创建主线程所需的 Looper
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
// 开启循环处理消息
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}那是如何处理消息的呢?
ActivityThread 中有一个内部类 H 继承了 Handler:
内部定义了所有消息的类型,以及如何去处理这些消息(重写了
handleMessage)
// ActivityThraead#H
class H extends Handler {
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int RECEIVER = 113;
...
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
...
break;
case ...
}
...
}EXIT_APPLICATION:应用退出,就是退出 Looper 的循环;case EXIT_APPLICATION: if (mInitialApplication != null) { mInitialApplication.onTerminate(); } Looper.myLooper().quit(); break;RESUME_ACTIVITY:就是 Activity的onResume生命周期(新版本的源码似乎没有),最终调用的是ActivityThread#handleResumeActivity...
回到正题,Looper中的死循环堵塞主线程,但是主线程中所有事件操作都是由 Looper 进行读取;
当 Looper 堵塞在 MessageQueue.next() 时,说明当前应用没有事件发生,换句话说,就是在休眠状态,并不是卡死状态;
而卡死状态是 ANR,当应用超过5秒没有响应输入事件时才会发生的;
当 Handler.dispatcheMessage 中执行耗时操作,导致其他输入事件没有响应时,才会发生ANR;
MessageQueue#next()堵塞时新加入的消息如何处理?
MessageQueue#next 堵塞实际是调用了 Native 层的 nativePollOnce() 方法进行精准时间的堵塞
在 Native 层,将进入
pullInner()方法,使用epoll_wait阻塞等待以读取管道的通知。如果没有从 Native 层得到消息,那么这个方法就不会返回。
此时主线程会释放 CPU 资源进入休眠状态。
当有消息加入时,会调用 MessageQueue#enqueue() 添加消息,在必要时(当新加入的消息需要插入到头部时)调用 nativeWake() 方法去唤醒:
向管道中写入一个消息,结束上述的堵塞,触发上面的
nativePollOnce()方法返回进而能继续处理新加入的消息
Message使用
obtain & recycle
Message 使用了 享元设计模式,内部维护了一个池(基于单链表实现的)
private static Message sPool; // 单链表的头每次获取对象时不建议使用 new 创建,而是使用 Message#obtain 来获取一个对象:
避免内存抖动,并回收复用已有的对象;
// 从内部维护的池
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}Message#recycle 将 Message 对象清空并加入到内部维护的池
public void recycle() {
// 检查该 Message 是否在使用;
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}Message#recycleUnchecked 将 Message 置空并加入到内部维护的池:
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}参数
what :(int) 用户自定义的消息码,用于区分每个消息的类型
arg1 和 arg2 :(int)如果只需要存储整型数据,则使用这两个字段,而不用 setData
when :(long,不可以自己设置)表示该消息传递给 target 的时间点
data :(Bundle)消息中包含的数据
同步屏障
Handler 中的Message 可以分为两类:同步消息、异步消息;
通过
Message#setAsynchronous(boolean)可以设置消息的同步或者异步:public void setAsynchronous(boolean async) { if (async) { flags |= FLAG_ASYNCHRONOUS; } else { flags &= ~FLAG_ASYNCHRONOUS; } }通过
Message#isAsynchronous()可以判断消息是否为异步:public boolean isAsynchronous() { return (flags & FLAG_ASYNCHRONOUS) != 0; }
其实还有第三种消息——屏障消息:
target==null
在一般情况下,两种消息没有什么差别;
但是在设置了 同步屏障 之后,可以拦截 Looper 对同步消息的获取和分发;
Looper只会获取和处理异步消息;
没有异步消息时会进入堵塞状态;
简单来说,同步屏障为 Handler 消息机制添加了一种简单的优先级机制,异步消息的优先级要高于同步消息;
设置同步屏障
通过 MessageQueue#postSyncBarrier 设置同步屏障(该方法有 @UnsupportedAppUsage注解修饰,不能直接调用,需要通过反射)
实际上是向队列中发送了一个消息,这个消息没有指定
target在设置屏障的时间点前的所有同步消息都会被处理,在设置屏障后所有同步消息都会被推迟执行(直到移除屏障后才会执行)
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
// 插入到队首
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
// 插入到队中
msg.next = p;
mMessages = msg;
}
return token;
}
}该方法会返回一个代表当前屏障的 token 数值,需要通过 MessageQueue#removeSyncBarrier(int) 传入该 token 才能移除对应的屏障消息;
从队列中找出对应
token的屏障消息并移除;这里和设置同步屏障不同,移除屏障后需要唤醒队列来及时处理后面的同步消息
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
// prev == null: 屏障消息位于队首,意味着当前没有异步消息等待处理
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}同步屏障是如何生效?
在 MessageQueue#next() 检索下一个出队的消息前,会先判断队首 msg 的 target 是否指定,上面设置屏障的消息就是没有设置 target 的;
因此就会进入下面的判断,检索队列中的 异步消息 ,跳过所有的同步消息;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
...
}如何发送异步消息?
在 Handler 的构造方法中,通常有 boolean async 参数,传入 true 表示 Handler 发送的消息都是异步消息;
在 Handler#enqueueMessage 中对消息类型进行设置:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
// 设置消息为异步消息
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}同步屏障应用
Android4.1之后增加了 Choreographer 机制,用于同 Vsync 机制配合,统一动画、输入和绘制时机。
当 UI 更新时,为了让主线程更快的响应 UI 更新的事件,通过设置同步屏障,让异步消息更快的得到处理
// ViewRootImpl#scheduleTraversals
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 开启同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 发送异步消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
// 上面发送内部消息的方法最终会调用到该方法
// Choreographer#postCallbackDelayedInternal
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
// 消息的类型设置为异步消息
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}