持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第30天,点击查看活动详情
接下来会出三篇文章详细介绍,View视图如何经过一遍遍的流程渲染到界面上的,从
Activity的onResume(),经过Choreographer处理,经过FrameDisplayEventReceiver处理,最终触发View的measure、layout、draw三大流程,将视图绘制到界面上。
历史文章
从onResume()分析,ViewRootImpl在视图渲染中扮演什么角色?
从postCallback()说起,Choreographer在视图渲染中扮演什么角色?
概述
上面一篇文章最后从postCallback()说起,Choreographer在视图渲染中扮演什么角色?我们最终讲到了FrameDisplayEventReceiver.run()方法,这个方法是收到底层脉冲信号后,onVsync()触发调用的,最终实现了View的三大渲染流程measure、layout、draw,接下来我们详细分析下。
从FrameDisplayEventReceiver.run()开始分析
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}
继续往下走调用到doFrame()方法,这个方法逻辑比较多,我们精简下只关注其中最核心的:
void doFrame(long frameTimeNanos, int frame,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
try {
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
}
}
有很多的doCallbacks()方法调用,每次调用不同的CALLBACK_XXX类型 ,还记得我们通过Choreographer.postCallback()方法添加刷新回调的逻辑吗,马上看下:
我们添加的视图渲染回调类型为CALLBACK_TRAVERSAL,对应上面第四个doCallbacks()方法的调用:
void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
//1.
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
}
try {
//2.
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
//3.
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
}
}
上面的代码就是已经精简之后的代码了,下面我们开始分析:
- 首先从
mCallbackQueues数组取出类型为CALLBACK_TRAVERSAL的对象,大家记得吗,我们之前的视图渲染回调就是放在这个mCallbackQueues数组中的。
最后拿到的是一个CallbackRecord类型,我们看下这个类的结构体:
这个对象的next属性指向下一个CallbackRecord对象,所以这是一个单链表结构,而action属性就是我们传入的视图渲染Runnable回调对象;
- 调用
CallbackRecord.run()方法开始执行:
对应的就是上图中的run()方法逻辑,其中当类型为CALLBACK_TRAVERSAL时,这个token对象是空的,所以会走下面的代码逻辑((Runnable)action).run()。
这里顺便说下什么时候token等于FRAME_CALLBACK_TOKEN呢,当调用postFrameCallbackDelayed()方法时,就会设置为FRAME_CALLBACK_TOKEN。
下面我们看下((Runnable)action).run()具体的逻辑,这个action对象的就是TraversalRunnable对象:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
继续看下doTraversal()方法:
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
首先移除了消息屏障,毕竟视图渲染完毕之后就没必要添加消息屏障了,阻碍其他任务的执行;
其次调用了方法performTraversals(),这个方法就是最最关键的方法了,真正的实现了视图三大流程measure、layout、draw的调用:
void performTranversals() {
performMeasure();
performLayout();
performDraw();
}
这个方法600多行代码,找出上面三行关键代码是真的不容易,根据方法名就可以知道,分别代表视图的三大渲染流程measure、layout、draw分发执行;
- 结束上面的渲染执行流程后,调用方法
recycleCallbackLocked()回收当前执行的CallbackRecord对象:
private void recycleCallbackLocked(CallbackRecord callback) {
callback.action = null;
callback.token = null;
callback.next = mCallbackPool;
mCallbackPool = callback;
}
最后放到了缓存对象mCallbackPool中。
分析完毕。
其他注意事项
-
注册底层脉冲信号之后想要,想要继续接受后续的脉冲信号,需要重新注册,也就是说你注册一次接下来只会生效一次,想要继续接受脉冲信号,那就继续注册;
-
借助
postFrameCallbackDelayed()方法我们可以实现一个简单的帧率监听,比如1s中是否刷新了60次,像属性动画的播放就是依靠该方法注册监听回调实现的;
总结
本篇文章主要分析了收到底层脉冲信号后,从DisplayEventReceiver.onVsync()开始一步步怎么触发界面的绘制渲染流程的,结合其他的两篇文章观看,相信你会对整个View的上屏流程有一个更加清晰的认知,希望这三篇文章能对你有所帮助。