Android6.0WMS(六)WMS动漫管理方法

摘要: Android的运用起动时,或是转换Activity时都是以动漫的方法显示信息前后左右两屏的转换全过程。动漫的基本原理非常简单,把一帧一帧的图象按一定时执行间间距显示信息出去就进行了...

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)( / getRefreshRate());//测算更新的時间间距
 mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
 for (int i = 0; i = CALLBACK_LAST; i++) {
 mCallbackQueues[i] = new CallbackQueue();
 }
FrameDisplayEventReceiver接纳VSync数据信号

大家在kc/article/details/( Android6.0 VSync数据信号怎样到客户过程 )这篇blog早已剖析过FrameDisplayEventReceiver的基本原理了,当VSync数据信号回来时,最终会启用到FrameDisplayEventReceiver类的onVsync涵数:

 @Override
 public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
 if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
 scheduleVsync();
 return;

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 mit 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 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 (essibilityController != null
 displayId == Display.DEFAULT_DISPLAY) {
 essibilityController.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 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方式提前准备下一帧。

提前准备一帧动漫的時间能够超越好几个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;

}

该方式的工作中便是设定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);
 ......
 }

仅有测算出的数据信息和上一频次据不一样才会启用showSurfaceRobustlyLocked輸出动漫帧。



联系我们

全国服务热线:4000-399-000 公司邮箱:343111187@qq.com

  工作日 9:00-18:00

关注我们

官网公众号

官网公众号

Copyright?2020 广州凡科互联网科技股份有限公司 版权所有 粤ICP备10235580号 客服热线 18720358503

技术支持:手机网页模板