赞
踩
Android窗口动画是Android系统动画的一种,当一个非Activity主窗口,非壁纸窗口添加或者移除的时候会触 发Android窗口动画的流程,举个例子 Toast的弹出或者移除的过程中就会触发窗口动画。
我把窗口动画的执行流程大概分为以下几步
在WMS中最重要的函数performSurfacePlacementNoTrace()后期,会遍历一块屏幕上所有的窗口(WindowState),如果该WindowState有surfece的话,会调用到WindowState.winAnimator.ommitFinishDrawingLocked()方法:
WindowStateAnimator.java boolean commitFinishDrawingLocked() { if (DEBUG_STARTING_WINDOW_VERBOSE && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) { Slog.i(TAG, "commitFinishDrawingLocked: " + mWin + " cur mDrawState=" + drawStateToString()); } if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) { return false; } ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s", mSurfaceController); mDrawState = READY_TO_SHOW; boolean result = false; final ActivityRecord activity = mWin.mActivityRecord; if (activity == null || activity.canShowWindows() || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) { //进一步到这里 result = mWin.performShowLocked(); } return result; }
如果是新添加的窗口则会通过mWinAnimator.applyEnterAnimationLocked();来触发窗口 进入动画。
WindowState.java boolean performShowLocked() { ...... logPerformShow("Showing "); mWmService.enableScreenIfNeededLocked(); // 如果新添加进来的窗口,则会在这里触发窗口动画。 mWinAnimator.applyEnterAnimationLocked(); // Force the show in the next prepareSurfaceLocked() call. mWinAnimator.mLastAlpha = -1; ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this); mWinAnimator.mDrawState = HAS_DRAWN; mWmService.scheduleAnimationLocked(); if (mHidden) { mHidden = false; final DisplayContent displayContent = getDisplayContent(); for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowState c = mChildren.get(i); if (c.mWinAnimator.mSurfaceController != null) { c.performShowLocked(); // It hadn't been shown, which means layout not performed on it, so now we // want to make sure to do a layout. If called from within the transaction // loop, this will cause it to restart with a new layout. if (displayContent != null) { displayContent.setLayoutNeeded(); } } } } return true; }
如下在窗口动画的处罚过程中if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper) {
applyAnimationLocked(transit, true);
} 就决定了只会给 非Activity主窗口和非壁纸类型的窗口添加窗口进入动画。
WindowStateAnimator.java void applyEnterAnimationLocked() { // If we are the new part of a window replacement transition and we have requested // not to animate, we instead want to make it seamless, so we don't want to apply // an enter transition. if (mWin.mSkipEnterAnimationForSeamlessReplacement) { return; } final int transit; // 在窗口的添加过程中 mEnterAnimationPending 已经被置为True,所以transit = WindowManagerPolicy.TRANSIT_ENTER; if (mEnterAnimationPending) { mEnterAnimationPending = false; transit = WindowManagerPolicy.TRANSIT_ENTER; } else { transit = WindowManagerPolicy.TRANSIT_SHOW; } // We don't apply animation for application main window here since this window type // should be controlled by ActivityRecord in general. Wallpaper is also excluded because // WallpaperController should handle it. //可以知道窗口进入动画不适用于 非activity的窗口 和 壁纸 if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper) { applyAnimationLocked(transit, true); } if (mService.mAccessibilityController.hasCallbacks()) { mService.mAccessibilityController.onWindowTransition(mWin, transit); } }
接下来就是根据场景添加添加对应的窗口动画类型,有定制的话,则采用定制的窗口动画类型。
WindowStateAnimator.java boolean (int transit, boolean isEntrance) { if (mWin.isAnimating() && mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running // an animation of the same type, then just leave that one alone. return true; } final boolean isImeWindow = mWin.mAttrs.type == TYPE_INPUT_METHOD; // 如果是输入法窗口 特殊处理 if (isEntrance && isImeWindow) { mWin.getDisplayContent().adjustForImeIfNeeded(); mWin.setDisplayLayoutNeeded(); mService.mWindowPlacerLocked.requestTraversal(); } // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. // 正常情况下 mWin.mToken.okToAnimate()) 为true if (mWin.mToken.okToAnimate()) { // 这个方法中 会对 StatusBar的窗口 和 NavigationBar窗口 进行特殊处理,如果是Toast 则返回DisplayPolicy.ANIMATION_STYLEABLE) int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit); int attr = -1; Animation a = null; if (anim != DisplayPolicy.ANIMATION_STYLEABLE) { if (anim != DisplayPolicy.ANIMATION_NONE) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation"); a = AnimationUtils.loadAnimation(mContext, anim); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } else { switch (transit) { // Toast添加会进入到该分支 case WindowManagerPolicy.TRANSIT_ENTER: attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation; break; case WindowManagerPolicy.TRANSIT_EXIT: attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation; break; case WindowManagerPolicy.TRANSIT_SHOW: attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation; break; case WindowManagerPolicy.TRANSIT_HIDE: attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation; break; } if (attr >= 0) { // 根据选择的动画参数 生成 Animation a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr( mWin.mAttrs, attr, TRANSIT_OLD_NONE); } } if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) { ProtoLog.v(WM_DEBUG_ANIM, "applyAnimation: win=%s" + " anim=%d attr=0x%x a=%s transit=%d type=%d isEntrance=%b Callers %s", this, anim, attr, a, transit, mAttrType, isEntrance, Debug.getCallers(20)); } if (a != null) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation"); // 窗口根据生成的对应类型的Animation ,来进一步触发动画 mWin.startAnimation(a); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); mAnimationIsEntrance = isEntrance; } } else if (!isImeWindow) { mWin.cancelAnimation(); } if (!isEntrance && isImeWindow) { mWin.getDisplayContent().adjustForImeIfNeeded(); } return mWin.isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION); }
至此加载对应的动画资源的流程结束。接下来开始进一步触发,如下对动画的参数进行了进一步设置,同时把 WindowAnimationSpec(动画规格)和mWmService.mSurfaceAnimationRunner(用来运行动画)封装到了一个LocalAnimationAdapter对象里
WindowState.java void startAnimation(Animation anim) { // If we are an inset provider, all our animations are driven by the inset client. if (mControllableInsetProvider != null) { return; } final DisplayInfo displayInfo = getDisplayInfo(); // 对动画的一些参数进行初始化,一些标志位进行初始化 anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(), displayInfo.appWidth, displayInfo.appHeight); // 给动画设置duiration anim.restrictDuration(MAX_ANIMATION_DURATION); // 设置动画缩放比例 anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked()); // 在这里创建了一个 LocalAnimationAdapter 对象 final AnimationAdapter adapter = new LocalAnimationAdapter( new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */, 0 /* windowCornerRadius */), mWmService.mSurfaceAnimationRunner); // 进一步启动动画 startAnimation(getPendingTransaction(), adapter); commitPendingTransaction(); } private void startAnimation(Transaction t, AnimationAdapter adapter) { startAnimation(t, adapter, mWinAnimator.mLastHidden, ANIMATION_TYPE_WINDOW_ANIMATION); } void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type) { startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */); } void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type) { startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */); } void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type, @Nullable OnAnimationFinishedCallback animationFinishedCallback) { startAnimation(t, anim, hidden, type, animationFinishedCallback, null /* adapterAnimationCancelledCallback */, null /* snapshotAnim */); }
如下调用到mSurfaceAnimator.startAnimation()来进一步启动动画。
但是需要注意的是 mSurfaceAnimator,为WindowContainer的一个属性, 类型为SurfaceAnimator,以下为SurfaceAnimator的类注释:
/**
* 一个可以运行动画在一些拥有子surfaces的 对象上,
* A class that can run animations on objects that have a set of child surfaces. We do this by
*
* reparenting all child surfaces of an object onto a new surface, called the "Leash". The Leash
* gets attached in the surface hierarchy where the the children were attached to. We then hand off
* the Leash to the component handling the animation, which is specified by the
* {@link AnimationAdapter}. When the animation is done animating, our callback to finish the
* animation will be invoked, at which we reparent the children back to the original parent.
*/
class SurfaceAnimator {
......
}
SurfaceAnimator引入了一个重要的概念:Leash,字面翻译为 “系带“,何为Leash?
WindowContainer.java
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim) {
ProtoLog.v(WM_DEBUG_ANIM, "Starting animation on %s: type=%d, anim=%s",
this, type, anim);
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}
SurfaceAnimator.java void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden, @AnimationType int type, @Nullable OnAnimationFinishedCallback animationFinishedCallback, @Nullable Runnable animationCancelledCallback, @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) { cancelAnimation(t, true /* restarting */, true /* forwardCancel */); mAnimation = anim; mAnimationType = type; mSurfaceAnimationFinishedCallback = animationFinishedCallback; mAnimationCancelledCallback = animationCancelledCallback; final SurfaceControl surface = mAnimatable.getSurfaceControl(); if (surface == null) { Slog.w(TAG, "Unable to start animation, surface is null or no children."); cancelAnimation(); return; } mLeash = freezer != null ? freezer.takeLeashForAnimation() : null; // 在这里会创建leash if (mLeash == null) { mLeash = createAnimationLeash(mAnimatable, surface, t, type, mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */, 0 /* y */, hidden, mService.mTransactionFactory); mAnimatable.onAnimationLeashCreated(t, mLeash); } mAnimatable.onLeashAnimationStarting(t, mLeash); if (mAnimationStartDelayed) { ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable); return; } // 创建完毕Leash 开始进一步的启动动画 mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback); if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); mAnimation.dump(pw, ""); ProtoLog.d(WM_DEBUG_ANIM, "Animation start for %s, anim=%s", mAnimatable, sw); } if (snapshotAnim != null) { mSnapshot = freezer.takeSnapshotForAnimation(); if (mSnapshot == null) { Slog.e(TAG, "No snapshot target to start animation on for " + mAnimatable); return; } mSnapshot.startAnimation(t, snapshotAnim, type); } }
如上,当startAnimation的mLeash为空的时候会去创建一个Leash,重点关注createAnimationLeash(),了解Leash是如何创建的。
SurfaceAnimator.java static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface, Transaction t, @AnimationType int type, int width, int height, int x, int y, boolean hidden, Supplier<Transaction> transactionFactory) { ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to leash for %s", animatable); final SurfaceControl.Builder builder = animatable.makeAnimationLeash() // 设置Leash的父节点为运行窗口动画的目标对象的SurfaceCrol的父节点 .setParent(animatable.getAnimationLeashParent()) .setName(surface + " - animation-leash of " + animationTypeToString(type)) // TODO(b/151665759) Defer reparent calls // We want the leash to be visible immediately because the transaction which shows // the leash may be deferred but the reparent will not. This will cause the leashed // surface to be invisible until the deferred transaction is applied. If this // doesn't work, you will can see the 2/3 button nav bar flicker during seamless // rotation. .setHidden(hidden) // 设置Leash的Surface类型为容器类型,即没有有用于渲染的Buffer。 .setEffectLayer() .setCallsite("SurfaceAnimator.createAnimationLeash"); final SurfaceControl leash = builder.build(); t.setWindowCrop(leash, width, height); t.setPosition(leash, x, y); t.show(leash); t.setAlpha(leash, hidden ? 0 : 1); // 把需要执行动画的SurafaControl又挂在Leash上 t.reparent(surface, leash); return leash; }
以上为Leah的创过程,其中最重要的就是三步:
简而言之,就是在执行动画的SuraceCntrol和它的父节点之间添加了一个类型为Leash的SurfaceControl,后面执行动画就直接操作这个Leash即可。
创建完毕Leash接下来通过 mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);来进一步启动动画。
LocalAnimationAdapter.java
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
() -> finishCallback.onAnimationFinished(type, this));
}
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, Runnable finishCallback) { synchronized (mLock) { final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, finishCallback); boolean requiresEdgeExtension = requiresEdgeExtension(a); ... ... if (!requiresEdgeExtension) { mPendingAnimations.put(animationLeash, runningAnim); if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) { mChoreographer.postFrameCallback(this::startAnimations); } // Some animations (e.g. move animations) require the initial transform to be // applied immediately. applyTransformation(runningAnim, t, 0 /* currentPlayTime */); } } } private void startAnimations(long frameTimeNanos) { synchronized (mLock) { if (!mPreProcessingAnimations.isEmpty()) { // We only want to start running animations once all mPreProcessingAnimations have // been processed to ensure preprocessed animations start in sync. // NOTE: This means we might delay running animations that require preprocessing if // new animations that also require preprocessing are requested before the previous // ones have finished (see b/227449117). return; } // 近一步启动 startPendingAnimationsLocked(); } mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0); }
@GuardedBy("mLock") private void startAnimationLocked(RunningAnimation a) { // 注意在这里创建了一个ValueAnimator的对象,后续动画的运行就要又它来处理 final ValueAnimator anim = mAnimatorFactory.makeAnimator(); // Animation length is already expected to be scaled. // 设置缩放比例 anim.overrideDurationScale(1.0f); // 设置持续时间 anim.setDuration(a.mAnimSpec.getDuration()); // 注册更新的监听,将会由ValueAnimator 不断触发以实现动画的效果、 anim.addUpdateListener(animation -> { synchronized (mCancelLock) { if (!a.mCancelled) { final long duration = anim.getDuration(); long currentPlayTime = anim.getCurrentPlayTime(); if (currentPlayTime > duration) { currentPlayTime = duration; } // 在这里会根据duration来真正操作surafce的属性,以实现窗口动画的效果 applyTransformation(a, mFrameTransaction, currentPlayTime); } } // Transaction will be applied in the commit phase. // 对surface属性的修改并不能立马生效,需要合并成一个事务,然后提交给surafeflinger。 scheduleApplyTransaction(); }); anim.addListener(new AnimatorListenerAdapter() { // 添加动画开始的监听 @Override public void onAnimationStart(Animator animation) { synchronized (mCancelLock) { if (!a.mCancelled) { // TODO: change this back to use show instead of alpha when b/138459974 is // fixed. mFrameTransaction.setAlpha(a.mLeash, 1); } } } // 添加动画结束的监听 @Override public void onAnimationEnd(Animator animation) { synchronized (mLock) { mRunningAnimations.remove(a.mLeash); synchronized (mCancelLock) { if (!a.mCancelled) { // Post on other thread that we can push final state without jank. mAnimationThreadHandler.post(a.mFinishCallback); } } } } }); a.mAnim = anim; // 把正在运行的动画添加到mRunningAnimations中 mRunningAnimations.put(a.mLeash, a); // 在这里会真正的开始启动动画。 anim.start(); if (a.mAnimSpec.canSkipFirstFrame()) { // If we can skip the first frame, we start one frame later. anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS); } // Immediately start the animation by manually applying an animation frame. Otherwise, the // start time would only be set in the next frame, leading to a delay. anim.doAnimationFrame(mChoreographer.getFrameTime()); }
可见最后驱动窗口执行动画是属性动画,但是这个属性动画比较特的是他的编舞者是Wms中专门用于系统动画的一个编舞者。
属性动画的驱动原理,这篇文章不重点介绍,
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。