本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。
前言
Android平台提供两种信号,一种是硬件信号,另一种是软件信号,由SurfaceFlinger进程的一个线程定时发出,硬件信号由硬件发出;
App进程若要通过gpu实现图像绘制,需要在接收到Vsync信号的条件下进行,因此,App进程访问SurfaceFlinger进程获取这个信号,再进行gpu绘制;
Android4.1之后增加了Choreographer机制,用于同Vsync机制配合,统一动画、输入和绘制时机;
Choreographer就是负责获取Vsync同步信号并控制App线程(主线程)完成图像绘制的类;
今天我们就来聊聊Choreographer机制;
一、Choreographer类介绍
1、实例初始化
public ViewRootImpl(Context context, Display display) {
...
//获取Choreographer实例
mChoreographer = Choreographer.getInstance();
...
}
public static Choreographer getInstance() {
return sThreadInstance.get();
}
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
return new Choreographer(looper);
}
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 每个线程中保存一个Choreographer实例对象;
- 线程本地存储ThreadLocal变量,Choreographer类型,在主线程中初始化变量时,创建Choreographer对象,绑定主线程Looper;
- 同一个App的每个窗体旗下ViewRootImpl使用的同一个Choregrapher对象,他控制者整个App中大部分视图的绘制节奏。
2、构造方法
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
//使用当前线程looper创建 mHandler
mHandler = new FrameHandler(looper);
//USE_VSYNC 4.1以上默认是true,表示 具备接受VSync的能力,这个接受能力就是FrameDisplayEventReceiver
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
// 计算一帧的时间,Android手机屏幕是60Hz的刷新频率,就是16ms
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
// 创建一个链表类型CallbackQueue的数组,大小为5,
//也就是数组中有五个链表,每个链表存相同类型的任务:输入、动画、遍历绘制等任务(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 有一个Looper和一个FrameHandler变量;
- 变量USE_VSYNC用于表示系统是否是用了Vsync同步机制,该值是通过读取系统属性debug.choreographer.vsync来获取的;
- 系统使用了Vsync同步机制,创建一个FrameDisplayEventReceiver对象用于请求并接收Vsync事件;
- Choreographer创建了一个大小为3的CallbackQueue队列数组,用于保存不同类型的Callback。
3、Callback类型
//输入事件,首先执行
public static final int CALLBACK_INPUT = 0;
//动画,第二执行
public static final int CALLBACK_ANIMATION = 1;
//插入更新的动画,第三执行
public static final int CALLBACK_INSETS_ANIMATION = 2;
//绘制,第四执行
public static final int CALLBACK_TRAVERSAL = 3;
//提交,最后执行,
public static final int CALLBACK_COMMIT = 4;
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
五种类型任务对应存入对应的CallbackQueue中;
每当收到 VSYNC 信号时,Choreographer 将首先处理 INPUT 类型的任务,然后是 ANIMATION 类型,最后才是 TRAVERSAL 类型。
4、FrameHandler处理的消息
- CallbackQueue是一个容量为4的数组,每一个元素作为头指针,引出对应类型的链表,4种事件就是通过这4个链表来维护的;
- 而FrameHandler中主要处理三类消息:
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync(); // 请求VSYNC信号
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- MSG_DO_FRAME 处理注册在Choreographer 的Runnable;
- MSG_DO_SCHEDULE_VSYNC 直接请求下一帧的VSync信号;
- MSG_DO_SCHEDULE_CALLBACK 根据Choreographer的配置执行合适的Handler延时处理;
二、Choreographer执行流程
1、requestLayout
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();//检查是否在当前线程
mLayoutRequested = true;//mLayoutRequested 是否measure和layout布局。
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {//同一帧内不会多次调用遍历
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//拦截同步Message
//Choreographer回调,执行绘制操作
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- postSyncBarrier : Handler 的同步屏障,它的作用是可以拦截 Looper 对同步消息的获取和分发,加入同步屏障之后,Looper 只会获取和处理异步消息,如果没有异步消息那么就会进入阻塞状态;
- Choreographer: 编舞者,统一动画、输入和绘制时机;
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
postCallback()->postCallbackDelayed()->postCallbackDelayedInternal():
- 1.
- 2.
- 3.
2、postCallbackDelayedInternal
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
// 当前时间
final long now = SystemClock.uptimeMillis();
// 回调执行时间,为当前时间加上延迟的时间
final long dueTime = now + delayMillis;
// obtainCallbackLocked(long dueTime, Object action, Object token)会将传入的3个参数转换为CallbackRecord(具体请看源码,非主要部分,此处略过),然后CallbackQueue根据回调类型将CallbackRecord添加到链表上。
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
// 如果delayMillis=0的话,dueTime=now,则会马上执行
scheduleFrameLocked(now);
} else {
// 如果dueTime>now,则发送一个what为MSG_DO_SCHEDULE_CALLBACK类型的定时消息,等时间到了再处理,其最终处理也是执行scheduleFrameLocked(long now)方法
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- mCallbackQueues先把对应的callback添加到链表上来,然后判断是否有延迟;
- 如果没有则会马上执行scheduleFrameLocked,如果有,则发送一个what为MSG_DO_SCHEDULE_CALLBACK类型的定时消息,等时间到了再处理;
- 其最终处理也是执行scheduleFrameLocked(long now)方法;
3、scheduleFrameLocked
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
// 如果使用了VSYNC,由系统值确定
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
if (isRunningOnLooperThreadLocked()) {
// 请求VSYNC信号,最终会调到Native层,Native处理完成后触发FrameDisplayEventReceiver的onVsync回调,回调中最后也会调用doFrame(long frameTimeNanos, int frame)方法
scheduleVsyncLocked();
} else {
// 在UI线程上直接发送一个what=MSG_DO_SCHEDULE_VSYNC的消息,最终也会调到scheduleVsyncLocked()去请求VSYNC信号
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
// 没有使用VSYNC
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
// 直接发送一个what=MSG_DO_FRAME的消息,消息处理时调用doFrame(long frameTimeNanos, int frame)方法
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
// Enable/disable vsync for animations and drawing.
private static final boolean USE_VSYNC = SystemProperties.getBoolean(
"debug.choreographer.vsync", true);
- 1.
- 2.
- 3.
常量USE_VSYNC,表示是否允许动画和绘制的垂直同步,默认是为true;
判断USE_VSYNC,如果使用了VSYNC:走scheduleVsyncLocked,即请求VSYNC信号,最终调用doFrame;
如果没使用VSYNC,则通过消息执行doFrame;
4、scheduleVsyncLocked
请求VSYNC信号的流程;
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- mDisplayEventReceiver 对应的是FrameDisplayEventReceiver,它继承自 DisplayEventReceiver , 主要是用来接收同步脉冲信号 VSYNC;
- scheduleVsync()方法通过底层nativeScheduleVsync()向SurfaceFlinger 服务注册,即在下一次脉冲接收后会调用 DisplayEventReceiver的dispatchVsync()方法;
- 这里类似于订阅者模式,但是每次调用nativeScheduleVsync()方法都有且只有一次dispatchVsync()方法回调;
- 底层向应用层发送VSYNC信号,java层通过dispatchVsync()接收,最后回调在FrameDisplayEventReceiver的onVsync;
5、FrameDisplayEventReceiver
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
//忽略来自第二显示屏的Vsync
if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
scheduleVsync();
return;
}
...
mTimestampNanos = timestampNanos;
mFrame = frame;
//该消息的callback为当前对象FrameDisplayEventReceiver
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
//此处mHandler为FrameHandler
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- onVsync()过程是通过FrameHandler向主线程Looper发送了一个自带callback的消息 callback为FrameDisplayEventReceiver;
- 当主线程Looper执行到该消息时,则调用FrameDisplayEventReceiver.run()方法,紧接着便是调用doFrame;
6、doFrame
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
...
//是否有跳帧,如果有那么就打印log并且修正偏差
}
//执行callback
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (DEBUG_FRAMES) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
doFrame方法做的就是渲染下一帧,检测是否卡顿并修补卡顿,然后开始做渲染工作,doCallbacks方法的参数:
- CALLBACK_INPUT:输入;
- CALLBACK_ANIMATION:动画;
- CALLBACK_TRAVERSAL:遍历,执行measure、layout、draw;
- CALLBACK_COMMIT:遍历完成的提交操作,用来修正动画启动时间;
7、doCallbacks
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
//从指定类型的CallbackQueue队列中查找执行时间到的CallbackRecord
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
}
try {
//由于CallbackQueues是按时间先后顺序排序的,因此遍历执行所有时间到的CallbackRecord
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
Choreographer内部维护了这四种链表,渲染每一帧的时候都会从上往下的去执行相应的渲染操作,有输入那么就先渲染输入队列,有动画就渲染动画,然后遍历,然后提交;
8、Choreographer总结
- 控制外部输入事件处理,动画执行,UI变化,以及提交执行都是在同一个类中做的处理,即是Choreographer;
- Choreographer支持4种类型事件:输入、绘制、动画、提交,并通过postCallback在对应需要同步vsync进行刷新处进行注册,等待回调;
- 每次执行的时候,Choreographer会根据当前的时间,只处理事件链表中最后一个事件,当有耗时操作在主线程时,事件不能及时执行,就会出现所谓的“跳帧”,“卡顿”现象;
- Choreographer的共有方法postCallback(callbackType, Object)是往事件链表中放事件的方法,而doFrame()是消耗这些事件的方法;
- Choreographer监听底层Vsync信号,一旦接收到回调信号,则通过doFrame统一对java层4种类型事件进行回调。