「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」
view的重绘过程首先会调用requestLayout方法。
屏幕的重绘示意图
底层刷新机制存在的缺陷。
Choreographer引入对这个进行了优化,只有在Vsync发送前完成view的重绘屏幕才会进行刷新。
Choreographer发一个消息等下一个Vsync信号来的时候去处理消息,使得VIew的重绘发生在Vsync信号到来后就立刻执行。
requestLayout
发起UI重绘
/frameworks/base/core/java/android/view/ViewRootImpl.java$requestLayout
@Override
public void requestLayout() {
//...
scheduleTraversals();
//..
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//postSyncBarrier加入同步屏障;同步屏障其实就是一个handler为空的消息,它也存放在消息队列中
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
没有直接绘制,做了两件事,
一个是往线程消息队列插入了一个SyncBarrier。其实是一个屏障,屏障后边的普通消息不能去处理,对异步消息没有影响。(有些消息比较紧急。)
另一个是向Choreographer的消息队列中插入了一个CALLBACK_TRAVERSAL,mTraversalRunnable等下一个Vsync信号来的时候要紧急处理这个消息。
mChoreographer的初始化创建在之前已经介绍过了。
mTraversalScheduled变量控制了重绘,在Vsync周期里面只会出现一次重绘。mTraversalRunnable进行重绘,当下一个Vsync信号来的时候才回去执行,将mTraversalScheduled设置为false,进而才可以接着requestLayout。
void doTraversal() {。
if (mTraversalScheduled) {
mTraversalScheduled = false;
//...
//进行真正的重绘
performTraversals();
//...
}
}
紧接着我们去看callback是如何加到Choreographer中的。
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
//根据callback类型插入到对应的单列表,根据duetime时间顺序去排序。
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);
}
}
}
mCallbackQueues数组中的每个类型都是一个callback单链表,根据callback类型插入到对应的单列表,根据callback的执行时间顺序去排序。
private void scheduleFrameLocked(long now) {
//...
//如果就是Choreographer的工作线程
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
//如果不是会发一个消息到Choreographer的工作线程
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);//异步消息不受屏障影响
//消息插入消息队列头部,告诉SurfaceFlinger在下一个Vsync信号来的时候进行通知
mHandler.sendMessageAtFrontOfQueue(msg);
}
//...
}
如果就是Choreographer的工作线程就直接执行scheduleVsyncLocked方法,如果不是的话,会发送一个消息到Choreographer的工作线程,而且发送的是一个异步消息不受屏障的影响。并且将消息插入消息队列头部,告诉SurfaceFlinger在下一个Vsync信号来的时候去通知我们。去请求这个Vsync信号,当拿到这个信号后有onVsync去响应,下篇文章继续去分析。