Android6.0 WMS(六) WMS动画管
理
Android的应用启动时,或者切换Activity时都会以动画的方式显示前后两屏的切换过程。动画的原理很简单,把一帧一帧的图像按一定时间间隔显示出来就完成了。
动画的绘制需要定时驱动,通常的做法是启动一个定时消息,每隔一定时间发一个消息,收到消息后输出一帧画面。Android支持VSync信号后,动画的驱动就有VSync信号承担了。 窗口动画的基本元素是窗口Surface中保存的图像,通过对窗口的Surface设置不同的变换矩阵和透明度,然后强制Surface刷新,就能在屏幕上显示出窗口的变化过程。
Choreographer对象初始化
我们先来看WMS中的mChoreographer 变量
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 final Choreographer mChoreographer = Choreographer.getInstance();
该变量是一个线程局部存储变量,在它的initialValue中创建了Choreographer对象并返回。这里使用线程局部存储的目录就是保证在线程中只有一个Choreographer对象。 [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 public static Choreographer getInstance() { return sThreadInstance.get(); }
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 private static final ThreadLocal
protected Choreographer initialValue() { Looper looper = Looper.myLooper(); if (looper == null) {
throw new IllegalStateException(\ }
return new Choreographer(looper); } };
再来看下Choreographer的构造函数,这里主要是创建了FrameDisplayEventReceiver用来接受VSync信号的对象。
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 private Choreographer(Looper looper) { mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;//接受VSync信号对象
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());//计算刷新的时间间隔
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); } }
FrameDisplayEventReceiver接受VSync信号
当VSync信号过来时,最后会调用到FrameDisplayEventReceiver类的onVsync函数: [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 @Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) { scheduleVsync(); return; }
long now = System.nanoTime(); if (timestampNanos > now) {
Log.w(TAG, \
+ \ Check that graphics HAL is generating vsync \ + \ timestampNanos = now; }
if (mHavePendingVsync) {
Log.w(TAG, \ There should only be \ + \ } else {
mHavePendingVsync = true; }
mTimestampNanos = timestampNanos; mFrame = frame;
Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); }
这主要是发送了一个信号,而是是Runnable的那种消息。
因此我们主要看下这个类的run函数,这里就是调用了Choreographer的doFrame函数。 [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 @Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame); }
doFrame函数
doFrame函数主要有一些VSync时间逻辑处理如果抛弃该VSync信号的话会调用scheduleVsyncLocked函数让SurfaceFlinger发送一个VSync信号,如果正常会调用4个doCallBacks函数。
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { ......
long intendedFrameTimeNanos = frameTimeNanos; startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos; if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos; final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; frameTimeNanos = startNanos - lastFrameOffset; }
if (frameTimeNanos < mLastFrameTimeNanos) { if (DEBUG_JANK) {
Log.d(TAG, \ May be due to a \ + \ Waiting for next vsync.\ }
scheduleVsyncLocked();//让SurfaceFlinger立马发送一个VSync信号 return; }
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos; }
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, \
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);//按键相关
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);//动画相关
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);//power相关
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
doCallbacks函数,我们首先会检查当前这个CallBackType是否有对应的CallBack回调,如果没有直接return,如果有的话会调用其回调的run函数。 [cpp] view plain copy 在CODE上查看代码片派生到我的代码片 void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) {
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); if (callbacks == null) {//没有对应CallBack回调 return; }
mCallbacksRunning = true;
// safe by ensuring the commit time is always at least one frame behind. if (callbackType == Choreographer.CALLBACK_COMMIT) { final long jitterNanos = now - frameTimeNanos;
Trace.traceCounter(Trace.TRACE_TAG_VIEW, \ if (jitterNanos >= 2 * mFrameIntervalNanos) {
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos + mFrameIntervalNanos; if (DEBUG_JANK) {
mDebugPrintNextFrameTimeDelta = true; }
frameTimeNanos = now - lastFrameOffset; mLastFrameTimeNanos = frameTimeNanos; } } } try {
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);//调用回调run函数 } } ...... }
这也就意味着当你没有CallBackType对应的回调,每次VSync信号过来到doFrame函数再到doCallBacks函数都是没有意义的。
WMS启动动画
那我们下面看在哪里把CallBackType对应的回调加入了,这里我们只关注动画相关的。 上面我们说到VSync会不断的发送,每秒60多次,但是动画不会不停的播放,就是这个CallBackType对应的回调没有。哪动画的启动和结束也就是受这个影响,而就是在WMS中调用scheduleAnimationLocked函数发起的动画启动。
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 void scheduleAnimationLocked() { if (!mAnimationScheduled) {
mAnimationScheduled = true;
mChoreographer.postFrameCallback(mAnimator.mAnimationFrameCallback); } }
这里就是调用Choreographer设置CallBackType,相关的回调。这里我们的callbackType是CALLBACK_ANIMATION
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 public void postFrameCallback(FrameCallback callback) { postFrameCallbackDelayed(callback, 0); }
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) { if (callback == null) {
throw new IllegalArgumentException(\ }
postCallbackDelayedInternal(CALLBACK_ANIMATION,
callback, FRAME_CALLBACK_TOKEN, delayMillis); }
我们最后看postCallbackDelayedInternal函数,就是在mCallBackQueues对应的CallBackType中增加相应的回调。这里也就是前面在WMS的scheduleAnimationLocked的参数mAnimator.mAnimationFrameCallback就是回调。
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { synchronized (mLock) {
final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis;