忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。
-- 服装学院的IT男
本篇为 StartWindow 的第二篇,执行时机是应用窗口要显示了,并且执行了 starting_reveal 动画后就不需要 StartWindow 了,这个时候需要将其移除。
根据 Winscope 看到的信息,移除流程还会触发“window_animation”动画。
1 初步分析触发时机
如何分析StartWindow的移除还是和以前一样可以有2种方式:
-
- 根据ShellStartingWindow的日志然后分析调用链
-
- 从业务逻辑分析
-
- 根据创建leash动画的堆栈开始分析
- ShellStartingWindow的日志分析
remove的日志开始是这段
ShellStartingWindow: Task start finish, remove starting surface for task: 28
ShellStartingWindow: Removing splash screen window for task: 28
在代码里找到这2句日志是在StartingSurfaceDrawer类下的removeStartingWindow和removeWindowSynced方法里调用的,这一块
TaskOrganizer.mInterface::removeStartingWindow ShellTaskOrganizer::removeStartingWindow StartingSurfaceDrawer::removeStartingWindow StartingSurfaceDrawer::removeWindowSynced
发现这个流程和【starting_reveal动画】前面的是一样的
- 业务逻辑分析
从业务逻辑上分析,StartWindow的作用就是在真正的window显示前为了过渡出现的,所以它的remove时间就是在真正Window要显示之前。而真正Window显示的节点就是starting_reveal的动画结束。 移除的第一个对应的日志应该的。
- 根据创建leash动画的堆栈开始分析 这个的堆栈如下,
09-29 21:31:03.361 17616 19545 E biubiubiu: SurfaceControl mName: Surface(name=bc9b727 Splash Screen com.google.android.dialer)/@0xf2e673e - animation-leash of window_animation mCallsiteSurfaceAnimator.createAnimationLeash
09-29 21:31:03.361 17616 19545 E biubiubiu: java.lang.Exception
09-29 21:31:03.361 17616 19545 E biubiubiu: at android.view.SurfaceControl.<init>(SurfaceControl.java:1580)
09-29 21:31:03.361 17616 19545 E biubiubiu: at android.view.SurfaceControl.<init>(Unknown Source:0)
09-29 21:31:03.361 17616 19545 E biubiubiu: at android.view.SurfaceControl$Builder.build(SurfaceControl.java:1240)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.SurfaceAnimator.createAnimationLeash(SurfaceAnimator.java:472)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.SurfaceAnimator.startAnimation(SurfaceAnimator.java:184)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2770)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2777)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2783)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.WindowState.startAnimation(WindowState.java:5377)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.WindowState.startAnimation(WindowState.java:5353)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.WindowStateAnimator.applyAnimationLocked(WindowStateAnimator.java:669)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.WindowManagerService.tryStartExitingAnimation(WindowManagerService.java:2638)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.WindowManagerService.relayoutWindow(WindowManagerService.java:2441)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.Session.relayout(Session.java:267)
09-29 21:31:03.361 17616 19545 E biubiubiu: at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:729)
09-29 21:31:03.361 17616 19545 E biubiubiu: at com.android.server.wm.Session.onTransact(Session.java:181)
09-29 21:31:03.361 17616 19545 E biubiubiu: at android.os.Binder.execTransactInternal(Binder.java:1285)
09-29 21:31:03.361 17616 19545 E biubiubiu: at android.os.Binder.execTransact(Binder.java:1244)
这一块是从 relayoutWindow 开始的,在 system_server 进程执行,是动画的主要调用链,但是需要找到什么时候触发的,才是 StartWindow 移除的真正触发点
综上第一第二点,StartWindow的移除触发是在【starting_reveal动画】结束的时候开始的,然后会触发一次relayoutWindow,进而执行动画
2 SystemUI进程移除StartWidow
StartingSurfaceDrawer::removeWindowInner WindowManagerGlobal::removeView WindowManagerGlobal::removeViewLocked ViewRootImpl::die ViewRootImpl::doDie ViewRootImpl::relayoutWindow IWindowSession::relayout --后续由system_server执行
这段前面的逻辑去【starting_reveal动画】看,当前直接从StartingSurfaceDrawer::removeWindowInner开始分析
# StartingSurfaceDrawer
private void removeWindowInner(View decorView, boolean hideView) {
if (mSysuiProxy != null) {
mSysuiProxy.requestTopUi(false, TAG);
}
if (hideView) {
// 设置GONE,这里的decorView就是startWindow的decorView
decorView.setVisibility(View.GONE);
}
// 触发remove,第二个参数为false
mWindowManagerGlobal.removeView(decorView, false /* immediate */);
}
# WindowManagerGlobal
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (root != null) {
root.getImeFocusController().onWindowDismissed();
}
// 调用ViewRootImpl::die 移除
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
// 加入需要移除的集合
mDyingViews.add(view);
}
}
}
# ViewRootImpl
boolean die(boolean immediate) {
if (ViewDebugManager.DEBUG_LIFECYCLE) {
Log.v(mTag, "die: immediate = " + immediate + ", mIsInTraversal = " + mIsInTraversal
+ ",mIsDrawing = " + mIsDrawing + ",this = " + this, new Throwable());
}
// Make sure we do execute immediately if we are in the middle of a traversal or the damage
// done by dispatchDetachedFromWindow will cause havoc on return.
//immediate为false左右不走这
if (immediate && !mIsInTraversal) {
doDie();
return false;
}
if (!mIsDrawing) {
destroyHardwareRenderer();
} else {
Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
// 发送消息
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
private void handleMessageImpl(Message msg) {
......
case MSG_DIE: {
doDie();
}
......
}
这里通过Handler发送消息,对于这个消息的处理也是只执行了doDie()方法,所以直接看这个方法就好了
# ViewRootImpl
void doDie() {
......
if (mView != null) {
// 前面设置为GONE了
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
// If layout params have been changed, first give them
// to the window manager to make sure it has the correct
// animation info.
try {
// 重点* 调用relayoutWindow
if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mWindowSession.finishDrawing(
mWindow, null /* postDrawTransaction */, Integer.MAX_VALUE);
}
} catch (RemoteException e) {
Log.e(mTag, "RemoteException when finish draw window " + mWindow
+ " in " + this, e);
}
}
destroySurface();
}
......
}
这段方法主要就是调用了relayoutWindow,参数viewVisibility是 GONE
# ViewRootImpl
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
......
relayoutResult = mWindowSession.relayout(mWindow, params,
requestedWidth, requestedHeight, viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mRelayoutBundle);
......
}
上面的这段逻辑,主要就是将startWindow进行了移除,然后通知WMS,执行一次relayout,后面就会触发startWindow的移除动画了。 至于怎么最终确认是这次SystemUI进程调用的relayout触发的呢? 在2编加上log或者直接debug都可以确认。
3 system_server 进程执行移除StartWindow动画
这块的堆栈在前面总结了,是某次relayoutWindow触发的,也跟过代码确认了是在starting_reveal动画结束后触发的startWindow移除,然后执行了一次relayoutWindow,然后调用到 system_server 进程触发这小节后续的逻辑
3.1 window_animation 动画图层的创建
整理调用链:
Session::relayout
WindowManagerService::relayoutWindow
WindowManagerService::tryStartExitingAnimation
WindowStateAnimator::applyAnimationLocked
DisplayPolicy::selectAnimation --选择动画资源
WindowState::startAnimation
WindowState::startAnimation
WindowContainer::startAnimation
WindowContainer::startAnimation
SurfaceAnimator::startAnimation
SurfaceAnimator::createAnimationLeash -- 创建leash图层
LocalAnimationAdapter::startAnimation -- 开始动画(本地动画) -- 各个adapter 分歧
SurfaceAnimationRunner::startAnimation
LocalAnimationAdapter::startAnimation
LocalAnimationAdapter::startAnimations
LocalAnimationAdapter::startPendingAnimationsLocked
LocalAnimationAdapter::startAnimationLocked
LocalAnimationAdapter::scheduleApplyTransaction --update每次执行
SurfaceAnimationRunner.mApplyTransactionRunnable::run
SurfaceAnimationRunner::applyTransaction --执行Transaction的apply
RunningAnimation.mFinishCallback::run --onAnimationEnd执行
开始撸代码:
# WindowManagerService
private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator,
boolean focusMayChange) {
// 本来是退出 退出类型,然后发现当前是StartWindow的退出,则改为TRANSIT_PREVIEW_DONE
int transit = WindowManagerPolicy.TRANSIT_EXIT;
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
String reason = null;
if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
reason = "applyAnimation";
focusMayChange = true;
win.mAnimatingExit = true;
} else ......//省略其他case
......
if (reason != null) {
// 打印日志
ProtoLog.d(WM_DEBUG_ANIM, "Set animatingExit: reason=startExitingAnimation/%s win=%s",
reason, win);
}
......
}
# WindowStateAnimator
boolean applyAnimationLocked(int transit, boolean isEntrance) {
......
if (mWin.mToken.okToAnimate()) {
// 重点*1 根据transit选择动画资源
int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
int attr = -1;
Animation a = null;
//remove startWindow走这
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) {
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) {
a = mWin.getDisplayContent().mAppTransition.loadAnimationAttr(
mWin.mAttrs, attr, TRANSIT_OLD_NONE);
}
}
// 重点*2 输出关键日志
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");
// 重点*3 开始动画
mWin.startAnimation(a);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
mAnimationIsEntrance = isEntrance;
}
} ......
......
}
3.2 动画资源和日志
看方法前面选择动画资源的部分
# DisplayPolicy
int selectAnimation(WindowState win, int transit) {
// 打印日志
ProtoLog.i(WM_DEBUG_ANIM, "selectAnimation in %s: transit=%d", win, transit);
......// 忽略其他case
// 当前逻辑在这
if (transit == TRANSIT_PREVIEW_DONE) {
if (win.hasAppShownWindows()) {
if (win.isActivityTypeHome()) {
// Dismiss the starting window as soon as possible to avoid the crossfade out
// with old content because home is easier to have different UI states.
return ANIMATION_NONE;
}
// 打印退出 startWindow
ProtoLog.i(WM_DEBUG_ANIM, "**** STARTING EXIT");
// 返回退出的动画资源
return R.anim.app_starting_exit;
}
}
return ANIMATION_STYLEABLE;
}
对应的动画资源如下:
# R.anim.app_starting_exit
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true"
android:interpolator="@interpolator/linear"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="150" />
这个默认的退出动画很简单,就是150毫秒内透明度变化,然后去根据winscope这段的动画对比了一下,确实只有透明度的变化 这部分的日志输出如下: 比较长因为还打印了调用链:
Line 1605: 09-29 21:31:02.632 17616 17644 V WindowManager: applyAnimation: win=WindowStateAnimator{d909ec3 Splash Screen com.google.android.dialer} anim=0 attr=0x0 a=null transit=1 type=3 isEntrance=true Callers com.android.server.wm.WindowStateAnimator.applyEnterAnimationLocked:597 com.android.server.wm.WindowState.performShowLocked:4721 com.android.server.wm.ActivityRecord.lambda$showAllWindowsLocked$16:6566 com.android.server.wm.ActivityRecord$$ExternalSyntheticLambda4.accept:2 com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply:2642 com.android.server.wm.WindowContainer$ForAllWindowsConsumerWrapper.apply:2632 com.android.server.wm.WindowState.applyInOrderWithImeWindows:4980 com.android.server.wm.WindowState.forAllWindows:4820 com.android.server.wm.WindowContainer.forAllWindows:1636 com.android.server.wm.WindowContainer.forAllWindows:1646 com.android.server.wm.ActivityRecord.showAllWindowsLocked:6564 com.android.server.wm.AppTransitionController.handleOpeningApps:1075 com.android.server.wm.AppTransitionController.handleAppTransitionReady:287 com.android.server.wm.RootWindowContainer.checkAppTransitionReady:981 com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace:846 com.android.server.wm.RootWindowContainer.performSurfacePlacement:788 com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop:178 com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement:126 com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement:115 com.android.server.wm.WindowSurfacePlacer$Traverser.run:57
Line 6054: 09-29 21:31:03.359 17616 19545 V WindowManager: applyAnimation: win=WindowStateAnimator{d909ec3 Splash Screen com.google.android.dialer} anim=17432595 attr=0xffffffffffffffff a=android.view.animation.AlphaAnimation@2a31554 transit=5 type=3 isEntrance=false Callers com.android.server.wm.WindowManagerService.tryStartExitingAnimation:2638 com.android.server.wm.WindowManagerService.relayoutWindow:2441 com.android.server.wm.Session.relayout:267 android.view.IWindowSession$Stub.onTransact:729 com.android.server.wm.Session.onTransact:181 android.os.Binder.execTransactInternal:1285 android.os.Binder.execTransact:1244 <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack> <bottom of call stack>
第一个应该是add流程,但是anim和a都没有实际的值,所以不会有后续动画图层的创建,第二个为remove,是有对应的动画的。
# WindowState
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);
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
// 重点* 这里创建的是本地动画的的adapter(LocalAnimationAdapter),注意第一个是WindowAnimationSpec对应
//第二个参数runer 是WMS的成员变量
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
mWmService.mSurfaceAnimationRunner);
// 带上adapter开始动画
startAnimation(getPendingTransaction(), adapter);
commitPendingTransaction();
}
private void startAnimation(Transaction t, AnimationAdapter adapter) {
// 然后就调用父类的方法了,注意type为 ANIMATION_TYPE_WINDOW_ANIMATION
startAnimation(t, adapter, mWinAnimator.mLastHidden, ANIMATION_TYPE_WINDOW_ANIMATION);
}
这里注意到,当前动画的是 window_animation ,用到的 Adapter 是 LocalAnimationAdapter,也就是常说的本地动画,在 system_server 进程执行的。而之前分析的 app_transition 是 RemoteAnimationAdapter(远端动画),在launcher进程执行的。
# WindowState
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
// 注意最后一个参数为null
startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback) {
// 最后一个参数为null
startAnimation(t, anim, hidden, type, animationFinishedCallback,
null /* adapterAnimationCancelledCallback */, null /* snapshotAnim */);
}
// 调用到这
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim) {
// 打印log
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.
// 倒数第二个参数传过来是null
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}
这一段的日志打印:
10-08 16:46:47.272 1529 1558 V WindowManager: Starting animation on Window{7d416db u0 Splash Screen com.google.android.dialer}: type=16, anim=com.android.server.wm.LocalAnimationAdapter@856484c
后面就是和之前app_transition动画一样调用到SurfaceAnimator了,这个方法和之前的还是一个的,创建动画图层,然后执行adapter的startAnimation。
# SurfaceAnimator
private AnimationAdapter mAnimation;
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
......
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;
}
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
......
}
创建动画图层之前看过了,区别是这次的type是前面传过来的ANIMATION_TYPE_WINDOW_ANIMATION,所以返回的类型就是"window_animation"。 然后下面的mAnimation前面也看到是LocalAnimationAdapter,和之前的也不一样。
4 开始remove动画
# LocalAnimationAdapter
private final SurfaceAnimationRunner mAnimator;
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
() -> finishCallback.onAnimationFinished(type, this));
}
mAnimator是在构建 LocalAnimationAdapter 时候传递的,根据前面的传参是在WMS中的成员变量mSurfaceAnimationRunner,而这个变量是在构造WMS的时候创建的,new的一个SurfaceAnimationRunner
# SurfaceAnimationRunner
// 一个等待做动画的集合
final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>();
final ArrayMap<SurfaceControl, RunningAnimation> mPreProcessingAnimations = new ArrayMap<>();
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
Runnable finishCallback) {
synchronized (mLock) {
......
// 创建RunningAnimation对象
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
finishCallback);
......
// 放入集合
mPreProcessingAnimations.put(animationLeash, runningAnim);
......
synchronized (mLock) {
......
// 将动画的leash图层和刚创建的runningAnim放入集合中
mPendingAnimations.put(animationLeash, runningAnim);
if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
// 下一帧执行startAnimations
mChoreographer.postFrameCallback(this::startAnimations);
}
}
}
}
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 startPendingAnimationsLocked() {
// 将mPendingAnimations所有的数据拿出来开始动画
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
startAnimationLocked(mPendingAnimations.valueAt(i));
}
mPendingAnimations.clear();
}
下面就是动画真正执行的地方了
# SurfaceAnimationRunner
private void startAnimationLocked(RunningAnimation a) {
final ValueAnimator anim = mAnimatorFactory.makeAnimator();
// Animation length is already expected to be scaled.
anim.overrideDurationScale(1.0f);
anim.setDuration(a.mAnimSpec.getDuration());
// 动画更新
anim.addUpdateListener(animation -> {
synchronized (mCancelLock) {
if (!a.mCancelled) {
final long duration = anim.getDuration();
long currentPlayTime = anim.getCurrentPlayTime();
if (currentPlayTime > duration) {
currentPlayTime = duration;
}
// 根据当时进度构建参数放到mFrameTransaction中
applyTransformation(a, mFrameTransaction, currentPlayTime);
}
}
// 应用mFrameTransaction
// Transaction will be applied in the commit phase.
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.
// 动画开始将leash的Alpha设置为1, 看上面的注释是为了修复出现的bug
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.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());
}
这段代码稍微有点长度,但是跟主流程的话,根据之前对远端动画的分析,主要关注动画的update是怎么处理的就好,还有最终是在哪里将Transformation执行apply。
4.1 动画的update处理
# SurfaceAnimationRunner
private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
// 这里的mSpec是构建LocalAnimationAdapter的第一个参数,所以就是WindowAnimationSpec
a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
}
// 下面的方法比较长,主要是根据当前动画的进度,然后计算出参数设置到Transaction中
# WindowAnimationSpec
@Override
public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
final TmpValues tmp = mThreadLocalTmps.get();
tmp.transformation.clear();
mAnimation.getTransformation(currentPlayTime, tmp.transformation);
tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
t.setAlpha(leash, tmp.transformation.getAlpha());
boolean cropSet = false;
if (mRootTaskClipMode == ROOT_TASK_CLIP_NONE) {
if (tmp.transformation.hasClipRect()) {
final Rect clipRect = tmp.transformation.getClipRect();
accountForExtension(tmp.transformation, clipRect);
t.setWindowCrop(leash, clipRect);
cropSet = true;
}
} else {
mTmpRect.set(mRootTaskBounds);
if (tmp.transformation.hasClipRect()) {
mTmpRect.intersect(tmp.transformation.getClipRect());
}
accountForExtension(tmp.transformation, mTmpRect);
t.setWindowCrop(leash, mTmpRect);
cropSet = true;
}
// We can only apply rounded corner if a crop is set, as otherwise the value is meaningless,
// since it doesn't have anything it's relative to.
if (cropSet && mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) {
t.setCornerRadius(leash, mWindowCornerRadius);
}
}
然后就要看看是哪里对Transaction进行apply的。
4.1.1 对动画的事务Transaction进行apply
# SurfaceAnimationRunner
private final Runnable mApplyTransactionRunnable = this::applyTransaction;
private void scheduleApplyTransaction() {
if (!mApplyScheduled) {
// 下一帧执行mApplyTransactionRunnable
mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable,
null /* token */);
mApplyScheduled = true;
}
}
下一帧要执行的mApplyTransactionRunnable其实就是执行当前类的方法applyTransaction。
# SurfaceAnimationRunner
private void applyTransaction() {
mFrameTransaction.setAnimationTransaction();
mFrameTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
// 重点* 最终执行apply
mFrameTransaction.apply();
mApplyScheduled = false;
}
至此,startWindow退出动画结束。