Android6.0 WMS WMS动画管理

丈夫志四海,万里犹比邻。这篇文章主要讲述Android6.0 WMS WMS动画管理相关的知识,希望能为你提供帮助。
android的应用启动时, 或者切换Activity时都会以动画的方式显示前后两屏的切换过程。动画的原理很简单, 把一帧一帧的图像按一定时间间隔显示出来就完成了。
动画的绘制需要定时驱动, 通常的做法是启动一个定时消息, 每隔一定时间发一个消息, 收到消息后输出一帧画面。Android支持VSync信号后, 动画的驱动就有VSync信号承担了。
窗口动画的基本元素是窗口Surface中保存的图像, 通过对窗口的Surface设置不同的变换矩阵和透明度, 然后强制Surface刷新, 就能在屏幕上显示出窗口的变化过程。


Choreographer对象初始化
我们先来看WMS中的mChoreographer 变量

final Choreographer mChoreographer = Choreographer.getInstance();

该变量是一个线程局部存储变量, 在它的initialValue中创建了Choreographer对象并返回。这里使用线程局部存储的目录就是保证在线程中只有一个Choreographer对象。
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); } };

再来看下Choreographer的构造函数, 这里主要是创建了FrameDisplayEventReceiver用来接受VSync信号的对象。

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信号
我们在http://blog.csdn.net/kc58236582/article/details/52892384( Android6.0 VSync信号如何到用户进程 ) 这篇博客已经分析过FrameDisplayEventReceiver的原理了, 当VSync信号过来时, 最后会调用到FrameDisplayEventReceiver类的onVsync函数:
@ 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, " Frame time is " + ((timestampNanos - now) * 0.000001f) + " ms in the future!Check that graphics HAL is generating vsync " + " timestamps using the correct timebase." ); timestampNanos = now; }if (mHavePendingVsync) { Log.w(TAG, " Already have a pending vsync event.There should only be " + " one at a time." ); } 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函数。

@ Override public void run() { mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame); }



doFrame函数
doFrame函数主要有一些VSync时间逻辑处理如果抛弃该VSync信号的话会调用scheduleVsyncLocked函数让SurfaceFlinger发送一个VSync信号, 如果正常会调用4个doCallBacks函数。

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, " Frame time appears to be going backwards.May be due to a " + " previously skipped frame.Waiting for next vsync." ); } scheduleVsyncLocked(); //让SurfaceFlinger立马发送一个VSync信号 return; }mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); mFrameScheduled = false; mLastFrameTimeNanos = frameTimeNanos; }try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, " Choreographer#doFrame" ); 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函数。

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, " jitterNanos" , (int) jitterNanos); 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函数发起的动画启动。
void scheduleAnimationLocked() { if (!mAnimationScheduled) { mAnimationScheduled = true; mChoreographer.postFrameCallback(mAnimator.mAnimationFrameCallback); } }

这里就是调用Choreographer设置CallBackType, 相关的回调。这里我们的callbackType是CALLBACK_ANIMATION
public void postFrameCallback(FrameCallback callback) { postFrameCallbackDelayed(callback, 0); }public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) { if (callback = = null) { throw new IllegalArgumentException(" callback must not be null" ); }postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN, delayMillis); }

我们最后看postCallbackDelayedInternal函数, 就是在mCallBackQueues对应的CallBackType中增加相应的回调。这里也就是前面在WMS的scheduleAnimationLocked的参数mAnimator.mAnimationFrameCallback就是回调。

private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; 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); } } }

我们来看下scheduleFrameLocked函数, 我们注意mFrameScheduled这个变量, 这个时候赋值为true, 后面就是用这个变量来控制每次VSync信号过来调用doFrame函数的时候是否要播放动画
private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; //注意这个变量 if (USE_VSYNC) { if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked(); //尽快让SurfaceFlinger中的EventThread发送一个VSync信号 } 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); if (DEBUG_FRAMES) { Log.d(TAG, " Scheduling next frame in " + (nextFrameTime - now) + " ms." ); } Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); } } }

我们再回过头来看doFrame函数, 当mFrameScheduled为false时, VSync信号过来该函数直接return不会播放动画。

void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { if (!mFrameScheduled) { return; // no work to do }



继续看postCallbackDelayedInternal函数中增加的回调, 这个回调在WindowAnimator的构造函数中就新建了Choreographer.FrameCallback回调

WindowAnimator(final WindowManagerService service) { mService = service; mContext = service.mContext; mPolicy = service.mPolicy; mAnimationFrameCallback = new Choreographer.FrameCallback() { public void doFrame(long frameTimeNs) { synchronized (mService.mWindowMap) { mService.mAnimationScheduled = false; animateLocked(frameTimeNs); } } }; }

我们最后看回调的run函数, 如果是FRAME_CALLBACK_TOKEN, 就是调用回调的doFrame函数。

private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; // Runnable or FrameCallback public Object token; public void run(long frameTimeNanos) { if (token = = FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos); } else { ((Runnable)action).run(); } } }


播放动画
在上面doFrame函数启动动画, 而动画的播放主要在WindowAnimator的animateLocked函数。
private void animateLocked(long frameTimeNs) { ...... boolean wasAnimating = mAnimating; mAnimating = false; //设置mAnimating为false mAppWindowAnimating = false; SurfaceControl.openTransaction(); SurfaceControl.setAnimationTransaction(); try { final int numDisplays = mDisplayContentsAnimators.size(); for (int i = 0; i < numDisplays; i+ + ) { final int displayId = mDisplayContentsAnimators.keyAt(i); updateAppWindowsLocked(displayId); ......// Update animations of all applications, including those // associated with exiting/removed apps updateWindowsLocked(displayId); updateWallpaperLocked(displayId); final WindowList windows = mService.getWindowListLocked(displayId); final int N = windows.size(); for (int j = 0; j < N; j+ + ) { windows.get(j).mWinAnimator.prepareSurfaceLocked(true); //输出动画帧 } }for (int i = 0; i < numDisplays; i+ + ) { final int displayId = mDisplayContentsAnimators.keyAt(i); testTokenMayBeDrawnLocked(displayId); final ScreenRotationAnimation screenRotationAnimation = mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation; if (screenRotationAnimation != null) { screenRotationAnimation.updateSurfacesInTransaction(); }mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers(); if (mService.mAccessibilityController != null & & displayId = = Display.DEFAULT_DISPLAY) { mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked(); } }if (mAnimating) {//为true, 继续调用WMS的scheduleAnimationLocked播放下一帧 mService.scheduleAnimationLocked(); }...... finally { SurfaceControl.closeTransaction(); }...... boolean doRequest = false; if (mBulkUpdateParams != 0) { doRequest = mService.copyAnimToLayoutParamsLocked(); }if (hasPendingLayoutChanges || doRequest) { mService.requestTraversalLocked(); //重新刷新UI }if (!mAnimating & & wasAnimating) { mService.requestTraversalLocked(); } }

animateLocked方法先将mAnimating 设置为false, 然后调用updateWindowsLocked函数和updateWallpaperLocked函数, updateWindowsLocked这个函数会调用WindowStateAnimator类的stepAnimationLocker方法, 如果动画已经显示完最后一帧, stepAnimationLocker方法将会WindowStateAnimator类的mAnimating设置为false, 表示该窗口的动画已经结束。而在updateWallpaperLocked函数中会判断所有窗口的动画是否已经结束, 只要有一个动画没结束, 就会将winAnimator的mAnimating设置为true。
for (int i = windows.size() - 1; i > = 0; i--) { final WindowState win = windows.get(i); WindowStateAnimator winAnimator = win.mWinAnimator; if (winAnimator.mSurfaceControl = = null) { continue; }final int flags = win.mAttrs.flags; if (winAnimator.mAnimating) { ...... mAnimating = true; } ......

再回到animatelocked方法, 当mAnimating为true是会调用WMS的scheduleAnimationLocked方法继续显示动画, 否则动画显示就结束了。

下面我们总结下动画的播放过程: 需要播放动画时, 先会调用WMS的scheduleAnimationLocked方法。调用这个方法后, 才会接受并处理一次VSync信号, 对VSync信号的处理, 就是所有需要绘制的窗口根据各自动画的谁知重新调整窗口Surface的变化矩阵和透明度; 如果还有窗口动画需要显示, 继续调用scheduleAnimationLocked方法准备下一帧。
【Android6.0 WMS WMS动画管理】准备一帧动画的时间可以跨越多个VSync信号周期, 但是只有收到VSync信号才能更新窗口的Surface的属性和内容, 对应用而言收到VSync信号意味着SurfaceFlinger中已经把上次设置的动画数据取走了, 可以安全地设置下一帧动画的属性和内容了。


窗口动画对象WindowStateAnimator
窗口对象WindowState中定义了一个类型为WindowStateAnimator的成员变量mWinAnimator, 用来表示窗口的动画对象。
下面是一些成员变量
boolean mAnimating; //表示是否正在显示动画 boolean mLocalAnimating; //表示窗口动画是否已经初始化 Animation mAnimation; //表示窗口动画对象 boolean mAnimationIsEntrance; // boolean mHasTransformation; //表示当前动画的mTransformation是否可用 boolean mHasLocalTransformation; //表示当前动画时一个窗口动画还是Activity动画 final Transformation mTransformation = new Transformation(); //变换矩阵对象

当前正在显示的动画有两种类型, 一种的窗口切换动画, 一种是Activity切换动画, 这里使用了mLocalAnimating和mHasLocalTransformation分别表示窗口动画的状态。
stepAnimationLocked是WindowStateAnimator类中显示动画首先调用的方法, 它会初始化WindowStateAnimator对象的一些成员变量
boolean stepAnimationLocked(long currentTime) { final DisplayContent displayContent = mWin.getDisplayContent(); if (displayContent != null & & mService.okToDisplay()) {if (mWin.isDrawnLw() & & mAnimation != null) {//窗口准备好绘制了, 窗口动画对象不为空 mHasTransformation = true; mHasLocalTransformation = true; if (!mLocalAnimating) {//还没有初始化窗口对象 final DisplayInfo displayInfo = displayContent.getDisplayInfo(); if (mAnimateMove) { mAnimateMove = false; mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),//初始化窗口对象 mAnimDw, mAnimDh); } else { mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(), displayInfo.appWidth, displayInfo.appHeight); } mAnimDw = displayInfo.appWidth; mAnimDh = displayInfo.appHeight; mAnimation.setStartTime(mAnimationStartTime != -1 ? mAnimationStartTime : currentTime); mLocalAnimating = true; // 设置为true代表已经初始化窗口对象 mAnimating = true; } if ((mAnimation != null) & & mLocalAnimating) { mLastAnimationTime = currentTime; if (stepAnimation(currentTime)) {//通过时间判断动画是否显示完毕 return true; } } } mHasLocalTransformation = false; if ((!mLocalAnimating || mAnimationIsEntrance) & & mAppAnimator != null//没有设置窗口动画或者窗口动画结束了 & & mAppAnimator.animation != null) { // 如果有Activity动画, 将mAnimating设为true mAnimating = true; mHasTransformation = true; mTransformation.clear(); return false; } else if (mHasTransformation) { // Little trick to get through the path below to act like // we have finished an animation. mAnimating = true; } else if (isAnimating()) { mAnimating = true; } } else if (mAnimation != null) { mAnimating = true; }if (!mAnimating & & !mLocalAnimating) { return false; }mAnimating = false; mKeyguardGoingAwayAnimation = false; mAnimatingMove = false; mLocalAnimating = false; ...... mHasLocalTransformation = false; ...... mTransformation.clear(); ......return false; }

该方法的工作就是设置WindowStateAnimator对象的几个成员变量, 首先调用WindowState对象的isDrawnLw来判断窗口系统的状态, 只有准备好了才能显示, 接着判断mAnimation是否为空, 不为空代表已经设置好了动画对象。
接下来判断mLocalAnimating变量的值, 为false则调用mAnimation的intialize方法来完成动画对象的初始化( 主要设置动画的高度和宽度) , 然后将mLocalAnimating和mAnimating设为true。完成初始化后, 接着调用stepAnimation方法来判断动画是否已经显示完成, 没有完成返回true。
如果没有设置动画或者动画已经结束了, 则还有判断窗口所在的Activity是否还存在动画, 如果有, 将mAnimating设置true( 表示还要继续播放动画) , 如果同时mHasTransformation的值仍然为true, 或者isAnimating方法返回true, 也将mAnimating设置为true。
isAnimating会根据当前动画对象mAnimation是否为空, 它的附加窗口的动画对象是否为空, 以及窗口所在的Activity的动画对象是否为空等条件来判断, 这表示只要有可能mAnimating就会设置为true。这样的目的尽量让动画完成显示, 即使没有可显示的动画, 多刷新几次不会有副作用, 但如果少画了一次, 屏幕上就可能留下不正确画面了。
boolean isAnimating() { return mAnimation != null || (mAttachedWinAnimator != null & & mAttachedWinAnimator.mAnimation != null) || (mAppAnimator != null & & mAppAnimator.isAnimating()); }



动画生成及显示

我们再看看动画的生成过程, WindowStateAnimator的prepareSurfaceLocked方法来完成计算一帧动画并显示工作:


public void prepareSurfaceLocked(final boolean recoveringMemory) { ...... computeShownFrameLocked(); //计算要显示的动画帧setSurfaceBoundariesLocked(recoveringMemory); if (mIsWallpaper & & !mWin.mWallpaperVisible) { hide(); //如果是壁纸窗口, 隐藏 } else if (w.mAttachedHidden || !w.isOnScreen()) { hide(); //如果窗口不可见, 隐藏 ...... } else if (mLastLayer != mAnimLayer || mLastAlpha != mShownAlpha || mLastDsDx != mDsDx || mLastDtDx != mDtDx || mLastDsDy != mDsDy || mLastDtDy != mDtDy || w.mLastHScale != w.mHScale || w.mLastVScale != w.mVScale || mLastHidden) {//每个值是否有变化 displayed = true; mLastAlpha = mShownAlpha; mLastLayer = mAnimLayer; mLastDsDx = mDsDx; mLastDtDx = mDtDx; mLastDsDy = mDsDy; mLastDtDy = mDtDy; w.mLastHScale = w.mHScale; w.mLastVScale = w.mVScale; if (mSurfaceControl != null) { try { mSurfaceAlpha = mShownAlpha; mSurfaceControl.setAlpha(mShownAlpha); mSurfaceLayer = mAnimLayer; mSurfaceControl.setLayer(mAnimLayer); mSurfaceControl.setMatrix( mDsDx * w.mHScale, mDtDx * w.mVScale, mDsDy * w.mHScale, mDtDy * w.mVScale); if (mLastHidden & & mDrawState = = HAS_DRAWN) { if (showSurfaceRobustlyLocked()) {//输出动画帧 mLastHidden = false; if (mIsWallpaper) { mService.dispatchWallpaperVisibility(w, true); } mAnimator.setPendingLayoutChanges(w.getDisplayId(), WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM); } else { w.mOrientationChanging = false; } } if (mSurfaceControl != null) { w.mToken.hasVisible = true; } } catch (RuntimeException e) { Slog.w(TAG, " Error updating surface in " + w, e); if (!recoveringMemory) { mService.reclaimSomeSurfaceMemoryLocked(this, " update" , true); } } } } ...... }

该函数先调用了computeShownFrameLocked函数计算当前需要显示的动画帧数据, mAnimLayer表示窗口的Z轴、mShownAlpha窗口透明度; mDsDx、mDtDx、mDsDy和mDtDy表示二维变换矩阵; w.mHScale w.mVScale表示窗口的缩放比例
只有计算出的数据和上一次数据不一样才会调用showSurfaceRobustlyLocked输出动画帧。


我们再来看WindowStateAnimator的prepareSurfaceLocked函数中下面一段代码, 会调用showSurfaceRobustlyLocked函数显示window, 当返回true代表成功, 会把mLastHidden为false, 代表上次没有隐藏。

if (showSurfaceRobustlyLocked()) { mLastHidden = false; if (mIsWallpaper) { mService.dispatchWallpaperVisibility(w, true); } // This draw means the difference between unique content and mirroring. // Run another pass through performLayout to set mHasContent in the // LogicalDisplay. mAnimator.setPendingLayoutChanges(w.getDisplayId(), WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM); } else { w.mOrientationChanging = false; }


    推荐阅读