一.首先介绍一些基础知识
1. 刷新率(Refresh Rate):
刷新率代表屏幕在一秒内刷新屏幕的次数,用赫兹来表示。赫兹是频率的单位,一秒震动的次数。这个刷新率取决于硬件固定的参数。这个值一般是60Hz。即每16.66ms刷新一次屏幕。
2.帧速率(Frame Rate ):
帧速率代表了GPU在一秒内绘制操作的帧数。比如30FPS、60FPS。Frame Per Second。
3.如果两个设备独立运行,如果刷新率和帧速率不同步,会引发以下两种问题。
如果帧速率高于刷新率,制作的频率大于展示的频率,会导致屏幕图像的展示的跳跃,跳帧的现象。
如果帧速率小于刷新率,制作的频率小于展示的频率,会导致屏幕图像的展示的中断,掉帧的现象。
4.android为了解决上面的问题,在4.1版本中引入了Projectbuffer.
ProjectBuffer对Android Display系统进行了重构,引入了三个核心元素,即Vsync,TripleBuffer和Choreographer。
其中Vsync是Vertical Synchronization 垂直同步是缩写。是一种在PC上已经很早就广泛使用的技术。
引入是Vsync来进行控制CPUGPU的绘制和屏幕刷新同步进行。
而编舞者choreography的引入,主要是配合Vsync,给上层App的渲染提供一个稳定的时机。Vsync到来的时候,Choreographer可以根据Vsync信号,统一管理应用的输入、动画、绘制等任务的执行情况。Choreographer就像一个指挥家一样,来把控着UI的绘制,所以取名编舞者。
二、android源码中Choreographer是如何运行的。
1.首先在ViewRootImpl构造函数中创建了Choreographer对象
public ViewRootImpl(Context context, Display display) {
mChoreographer = Choreographer.getInstance();
}
public static Choreographer getInstance() {
return sThreadInstance.get();
}
当调用get时,如果为null,会调用initialValue()方法。并把Choreographer实例和ThreadLocal绑定。
private static final ThreadLocal sThreadInstance =
new ThreadLocal() {
@Override
protected Choreographer initialValue() {
//因为后面会用到handler通讯,所以必须有一个Looper循环
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
//如果是主线程,则把choreographer赋值给mMainInstance
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
return choreographer;
}
};
2.看Choreographer构造函数
mLastFrameTimeNanos:记录上一帧绘制的时间。
mFrameIntervalNanos:屏幕绘制一帧的时间间隔,这个是纳秒值。如果屏幕刷新率是60Hz,那么刷新一帧的时间间隔就是16.66.......毫秒。
private Choreographer(Looper looper, int vsyncSource) {
// 传一个Looper进来,
mLooper = looper;
//用来处理消息的。
mHandler = new FrameHandler(looper);
//USE_VSYNC 是否使用Vsync
//boolean USE_VSYNC = SystemProperties.getBoolean("debug.choreographer.vsync", true);
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
//上一帧绘制的时间
mLastFrameTimeNanos = Long.MIN_VALUE;
//1秒是1000毫秒,1毫秒是1000微秒,1微秒是1000纳秒
//1秒就是1*1000*1000*1000=10的九次方纳秒
//绘制一帧的时间间隔----纳秒。如果是60Hz,那么刷新一帧展示的时间就是16.66毫秒。
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
//初始化回调队列,后面会从这个回调队列中取出Runnable执行run方法。
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
获取屏幕的刷新率:
//屏幕的刷新率,一秒钟可以刷新屏幕多少次,通常是60Hz
private static float getRefreshRate() {
DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(
Display.DEFAULT_DISPLAY);
return di.getMode().getRefreshRate();
}
3.初始化工作完成,那么Choreographer是怎么跑起来的,入口函数在哪?
对于UI绘制来说是入口在RootViewImpl的scheduleTraversals()方法中。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//发送一个屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//注意第一个参数CALLBACK_TRAVERSAL,回调函数的类型。
//mTraversalRunnable 回调函数要执行的runnable。
//第三个参数token,传了一个null
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
//第一个参数callbackType 有五种类型,这几个回调是有顺序的。 1.CALLBACK_INPUT 输入回调,首先运行 2.CALLBACK_ANIMATION 动画回调,这个在将动画原理的时候,会看到 3.CALLBACK_INSETS_ANIMATION inset和update相关的动画,运行在上面两个回调之后, 4.CALLBACK_TRAVERSAL 遍历回调,用于处理布局和绘制 5.CALLBACK_COMMIT Commit回调,在Traversal绘制回调之后。
接下来看postCallbackDelayedInternal方法
第二个参数就是上面的mTraversalRunnable。 第四个参数延迟的时间,这里延迟时间是0,没有延迟 所以这个方法走if判断的第一个分支,
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
//将runnable加入回调队列
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
上面传过来的delayMillis是0,所以走第一个分支。
if (dueTime <= now) {
scheduleFrameLocked(now);
} else { //如果有延迟,则发送一个延迟的异步消息。这种消息在handler同步屏障文章中介绍过
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
//如果使用垂直同步
if (USE_VSYNC) {
//判断是否运行在主线程,如果是则直接调用scheduleVsyncLocked()
//如果运行在子线程则通过发送handler 的方式也会调用到scheduleVsyncLocked()
if (isRunningOnLooperThreadLocked()) {//Looper.myLooper() == mLooper
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
}else{
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
scheduleVsyncLocked()方法。
private void scheduleVsyncLocked() {
//调用父类 DisplayEventReceiver的方法
mDisplayEventReceiver.scheduleVsync();
}
在scheduleVsync()方法中会调用nativeScheduleVsync,这是一个native方法,在native层执行完毕后会回调到java层的方法dispatchVsync()
scheduleVsync:向native层去请求一个Vsync信号。
dispatchVsync:请求到Vsync信号后,执行Java层的UI绘制和渲染逻辑。
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 { // 调用native 方法
//调用Native方法请求一个Vsync信号,然后会从native层回调java层的dispatchVsync方法
nativeScheduleVsync(mReceiverPtr);
}
}
timestampNanos:从Native层传递过来的一个时间戳,Vsync从native层发出的时间。
// Called from native code.
从native层回调java层的dispatchVsync方法
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
onVsync(timestampNanos, physicalDisplayId, frame);
}
在这又发送了一个异步消息,并且 Message.obtain(mHandler, this);第二个参数是一个callBack回调。所以没有handler的情况下,会执行这个回调函数。但是传的是this,所以就会执行this的run方法。这个this就是FrameDisplayEventReceiver的实例,在Choreographer的构造函数中初始化的。
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
mTimestampNanos = timestampNanos;
mFrame = frame;
//得到message 添加了一个回调函数,this,则会调用run方法
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
在FrameDisplayEventReceiver的run方法中,调用的doFrame方法
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
//sync信号发出的时间,
long intendedFrameTimeNanos = frameTimeNanos;
//当前的时间
startNanos = System.nanoTime();
//两者相减得到的时间差,就是底层消息通讯和回调所消耗的时间
final long jitterNanos = startNanos - frameTimeNanos;
//如果这个时间差大于了一帧的时间间隔。
if (jitterNanos >= mFrameIntervalNanos) {
//计算跳过了多少帧
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
//注意下面这行日子,如果跳帧大于30帧,系统会打印下面这行log,在主线程做了太多工作,会造成UI卡顿。
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
//取模,得到的值就是一帧多出来的时间
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
}
//用当前时间减去多出来的时间,就是下一帧要绘制的时间
//进行绘制时间的修正,保证每一次的绘制时间间隔都是mFrameIntervalNanos
frameTimeNanos = startNanos - lastFrameOffset;
}
//如果底层传过来的时间,小于上一帧绘制的时间,正常情况下,frameTimeNanos都是大于上一帧绘制的时间的。
if (frameTimeNanos < mLastFrameTimeNanos) {
//跳过本次的绘制,请求下一帧的时间
scheduleVsyncLocked();
return;
}
//以上的判断,都是为了控制绘制的频率。
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
scheduleVsyncLocked();
return;
}
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
//重置标志位,可以再次进入scheduleFrameLocked
mFrameScheduled = false;
//将底层传过来的时间,记录为本次绘制的时间,也就是下一帧传过来时,上一帧绘制的时间。
mLastFrameTimeNanos = frameTimeNanos;
}
try {
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
//根据Choreographer的CallBack类型,进行callBack的回调。
//输入
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
//动画
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
//界面绘制
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
//commit
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
这个是很重要的一个方法。
通过这个方法中的逻辑能够看出:Choreographer控制App层UI绘制的节奏和频率。
然后会按顺序执行一些列的doCallBacks函数。
首先会根据callbackType,从链表中取出CallBackRecord。然后再遍历CallBackRecord,调用他的run方法。
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
//根据callbacktype,从链表中拿到 CallbackRecord
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
for (CallbackRecord c = callbacks; c != null; c = c.next) {
//执行CallbackRecord的run方法
c.run(frameTimeNanos);
}
}
}
根据token来进行区分是FrameCallback类型还是Runnable。
主要这里的token传进来的是null,所以会执行else分支。
这个action就是 mTraversalRunnable,调用mTraversalRunnable的run方法。
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
在它的run方法中执行了doTraversal()。
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//删除屏障消息
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
//调用测量、布局和绘制方法
performTraversals();
}
}
performTraversals()方法中就会调用 performMeasure、performLayout、performDraw,对View进行测量、布局、和绘制。