行业知识
Android 13 WMS-动画流程
动���的类型如下
@IntDef(flag = true, prefix = { "ANIMATION_TYPE_" }, value = { ANIMATION_TYPE_NONE, ANIMATION_TYPE_APP_TRANSITION, ANIMATION_TYPE_SCREEN_ROTATION, ANIMATION_TYPE_DIMMER, ANIMATION_TYPE_RECENTS, ANIMATION_TYPE_WINDOW_ANIMATION, ANIMATION_TYPE_INSETS_CONTROL, ANIMATION_TYPE_TOKEN_TRANSFORM, ANIMATION_TYPE_STARTING_REVEAL }) ANIMATION_TYPE_APP_TRANSITION动画
当点击一个二级页面时, 会有如下调用栈
动画的type = ANIMATION_TYPE_APP_TRANSITION
03-05 14:55:37.752 3059 3163 D jinyanmeianimation: SurfaceAnimator startAnimation mAnimation:com.android.server.wm.LocalAnimationAdapter@71444ae03-05 14:55:37.752 3059 3163 D jinyanmeianimation: SurfaceAnimator startAnimation mLeash:Surface(name=Surface(name=ActivityRecord{8b5f0f2 u0 com.android.settings/.MainSettings} t76})/@0xaa929f3 - animation-leash of app_transition)/@0x9c116b003-05 14:55:37.752 3059 3163 D jinyanmeianimation: SurfaceAnimator startAnimation t:android.view.SurfaceControl$Transaction@8d1482403-05 14:55:37.752 3059 3163 D jinyanmeianimation: SurfaceAnimator startAnimation type:103-05 14:55:37.752 3059 3163 D jinyanmeianimation: SurfaceAnimator startAnimation mInnerAnimationFinishedCallback:com.android.server.wm.SurfaceAnimator$$ExternalSyntheticLambda0@d22724f03-05 14:55:37.752 3059 3163 D jinyanmeianimation: java.lang.RuntimeException: jinyanmeianimation03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.SurfaceAnimator.startAnimation(SurfaceAnimator.java:200)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.WindowContainer.startAnimation(WindowContainer.java:2912)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.WindowContainer$AnimationRunnerBuilder.lambda$build$com-android-server-wm-WindowContainer$AnimationRunnerBuilder(WindowContainer.java:4257)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.WindowContainer$AnimationRunnerBuilder$$ExternalSyntheticLambda4.startAnimation(Unknown Source:7)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.WindowContainer.applyAnimationUnchecked(WindowContainer.java:3384)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.WindowContainer.applyAnimation(WindowContainer.java:3072)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.ActivityRecord.applyAnimation(ActivityRecord.java:6074)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.AppTransitionController.applyAnimations(AppTransitionController.java:876)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.AppTransitionController.applyAnimations(AppTransitionController.java:1083)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.AppTransitionController.handleAppTransitionReady(AppTransitionController.java:293)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.RootWindowContainer.checkAppTransitionReady(RootWindowContainer.java:1070)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:937)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.RootWindowContainer.performSurfacePlacement(RootWindowContainer.java:877)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:199)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:148)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:137)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at com.android.server.wm.WindowSurfacePlacer$Traverser.run(WindowSurfacePlacer.java:79)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at android.os.Handler.handleCallback(Handler.java:942)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at android.os.Handler.dispatchMessage(Handler.java:99)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at android.os.Looper.loopOnce(Looper.java:211)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at android.os.Looper.loop(Looper.java:300)03-05 14:55:37.752 3059 3163 D jinyanmeianimation: at android.os.HandlerThread.run(HandlerThread.java:67) 每次循环都要检查是否开始一个动画
void handleAppTransitionReady() {183 mTempTransitionReasons.clear();// //检查app transition是否已经准备好184 if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)185 || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)186 || !transitionGoodToGoForTaskFragments()) {187 return;188 }189 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");190 191 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");192 // TODO(b/205335975): Remove window which stuck in animatingExit status. Find actual cause.193 mDisplayContent.forAllWindows(WindowState::cleanupAnimatingExitWindow,194 true /* traverseTopToBottom */);195 // TODO(new-app-transition): Remove code using appTransition.getAppTransition()196 final AppTransition appTransition = mDisplayContent.mAppTransition;197 198 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();199 200 appTransition.removeAppTransitionTimeoutCallbacks();201 202 mDisplayContent.mWallpaperMayChange = false;203 204 int appCount = mDisplayContent.mOpeningApps.size();205 for (int i = 0; i app.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS))233 || mDisplayContent.mClosingApps.stream().anyMatch(234 (app) -> app.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS));235 final ArraySet openingAppsForAnimation = getAppsForAnimation(236 mDisplayContent.mOpeningApps, excludeLauncherFromAnimation);237 final ArraySet closingAppsForAnimation = getAppsForAnimation(238 mDisplayContent.mClosingApps, excludeLauncherFromAnimation);239 240 @TransitionOldType final int transit = getTransitCompatType(241 mDisplayContent.mAppTransition, openingAppsForAnimation, closingAppsForAnimation,242 mDisplayContent.mChangingContainers,243 mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),244 mDisplayContent.mSkipAppTransitionAnimation);245 mDisplayContent.mSkipAppTransitionAnimation = false;246 247 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,248 "handleAppTransitionReady: displayId=%d appTransition={%s}"249 + " excludeLauncherFromAnimation=%b openingApps=[%s] closingApps=[%s] transit=%s",250 mDisplayContent.mDisplayId, appTransition.toString(), excludeLauncherFromAnimation,251 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,252 AppTransition.appTransitionOldToString(transit));253 254 // Find the layout params of the top-most application window in the tokens, which is255 // what will control the animation theme. If all closing windows are obscured, then there is256 // no need to do an animation. This is the case, for example, when this transition is being257 // done behind a dream window.258 final ArraySet activityTypes = collectActivityTypes(openingAppsForAnimation,259 closingAppsForAnimation, mDisplayContent.mChangingContainers);//首先会获取当前需要opening和closing的app window列表(ActivityRecord类型)260 final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes,261 openingAppsForAnimation, closingAppsForAnimation,262 mDisplayContent.mChangingContainers);263 final ActivityRecord topOpeningApp =264 getTopApp(openingAppsForAnimation, false /* ignoreHidden */);265 final ActivityRecord topClosingApp =266 getTopApp(closingAppsForAnimation, false /* ignoreHidden */);267 final ActivityRecord topChangingApp =268 getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);269 final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);270 271 // Check if there is any override272 if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {273 // Unfreeze the windows that were previously frozen for TaskFragment animation.274 unfreezeEmbeddedChangingWindows();275 overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);276 }277 278 final boolean voiceInteraction = containsVoiceInteraction(closingAppsForAnimation)279 || containsVoiceInteraction(openingAppsForAnimation);280 281 final int layoutRedo;282 mService.mSurfaceAnimationRunner.deferStartingAnimations();283 try {//然后在applyAnimations方法里面对window列表进行遍历WindowContainer的动画applyAnimation方法的调用284 applyAnimations(openingAppsForAnimation, closingAppsForAnimation, transit, animLp,285 voiceInteraction);286 handleClosingApps();287 handleOpeningApps();288 handleChangingApps(transit);289 290 appTransition.setLastAppTransition(transit, topOpeningApp,291 topClosingApp, topChangingApp);292 293 final int flags = appTransition.getTransitFlags();294 layoutRedo = appTransition.goodToGo(transit, topOpeningApp);295 handleNonAppWindowsInTransition(transit, flags);296 appTransition.postAnimationCallback();297 appTransition.clear();298 } finally {299 mService.mSurfaceAnimationRunner.continueStartingAnimations();300 }301 302 mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);303 304 mDisplayContent.mOpeningApps.clear();305 mDisplayContent.mClosingApps.clear();306 mDisplayContent.mChangingContainers.clear();307 mDisplayContent.mUnknownAppVisibilityController.clear();308 309 // This has changed the visibility of windows, so perform310 // a new layout to get them all up-to-date.311 mDisplayContent.setLayoutNeeded();312 313 mDisplayContent.computeImeTarget(true /* updateImeTarget */);314 315 mService.mAtmService.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(316 mTempTransitionReasons);317 318 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);319 320 mDisplayContent.pendingLayoutChanges |=321 layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;322 } applyAnimationUnchecked包含两部分,
第一先通过getAnimationAdapter加载合适的动画
如果是普通的窗口动画,比如app内部activity的切换,当前的场景是设置主菜单跳转子菜单,根据当前场景获取到具体的transit,transit=TRANSIT_OLD_ACTIVITY_OPEN,然后再结合enter为true或者false,可以最终可以找到设置主菜单的动画xml资源是activity_open_exit.xml,设置子菜单的动画xml资源是activity_open_enter.xml,在获取到具体xml资源名字后,通过AnimationUtils.loadAnimation方法把xml资源转成Animation对象。之后就会创建一个WindowAnimationSpec对象,并把Animation对象作为构造方法的第一个参数传给了WindowAnimationSpec
第二开始动画
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,2994 @TransitionOldType int transit, boolean isVoiceInteraction,2995 @Nullable ArrayList sources) {2996 final Task task = asTask();2997 if (task != null && !enter && !task.isActivityTypeHomeOrRecents()) {2998 final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);2999 final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null3000 && imeTarget.getWindow().getTask() == task;3001 // Attach and show the IME screenshot when the task is the IME target and performing3002 // task closing transition to the next task.3003 if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {3004 mDisplayContent.showImeScreenshot();3005 }3006 }3007 final Pair adapters = getAnimationAdapter(lp,3008 transit, enter, isVoiceInteraction);3009 AnimationAdapter adapter = adapters.first;3010 AnimationAdapter thumbnailAdapter = adapters.second;3011 if (adapter != null) {3012 if (sources != null) {3013 mSurfaceAnimationSources.addAll(sources);3014 }3015 3016 AnimationRunnerBuilder animationRunnerBuilder = new AnimationRunnerBuilder();3017 3018 if (isTaskTransitOld(transit)) {3019 animationRunnerBuilder.setTaskBackgroundColor(getTaskAnimationBackgroundColor());3020 // TODO: Remove when we migrate to shell (b/202383002)3021 if (mWmService.mTaskTransitionSpec != null) {3022 animationRunnerBuilder.hideInsetSourceViewOverflows(3023 mWmService.mTaskTransitionSpec.animationBoundInsets);3024 }3025 }3026 3027 final ActivityRecord activityRecord = asActivityRecord();3028 if (activityRecord != null && isActivityTransitOld(transit)3029 && adapter.getShowBackground()) {3030 final @ColorInt int backgroundColorForTransition;3031 if (adapter.getBackgroundColor() != 0) {3032 // If available use the background color provided through getBackgroundColor3033 // which if set originates from a call to overridePendingAppTransition.3034 backgroundColorForTransition = adapter.getBackgroundColor();3035 } else {3036 // Otherwise default to the window's background color if provided through3037 // the theme as the background color for the animation - the top most window3038 // with a valid background color and showBackground set takes precedence.3039 final Task arTask = activityRecord.getTask();3040 backgroundColorForTransition = ColorUtils.setAlphaComponent(3041 arTask.getTaskDescription().getBackgroundColor(), 255);3042 }3043 animationRunnerBuilder.setTaskBackgroundColor(backgroundColorForTransition);3044 }3045 3046 animationRunnerBuilder.build()3047 .startAnimation(getPendingTransaction(), adapter, !isVisible(),3048 ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);3049 3050 if (adapter.getShowWallpaper()) {3051 getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;3052 }3053 }3054 }3055 计算transition的类型
在getTransitCompatType中会对动画做转换,将动画转化为old类型的动画
@TransitionOldType static int getTransitCompatType(AppTransition appTransition, ArraySet openingApps, ArraySet closingApps, ArraySet changingContainers, @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) { Slog.d("jinyanmeianimation","getTransitCompatType appTransition :" + appTransition ); Slog.d("jinyanmeianimation","getTransitCompatType appTransition :" + appTransition , new RuntimeException("jinyanmeianimation")); // Determine if closing and opening app token sets are wallpaper targets, in which case // special animations are needed. final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps) && wallpaperTarget != null; final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps) && wallpaperTarget != null; // Keyguard transit has highest priority. switch (appTransition.getKeyguardTransition()) { case TRANSIT_KEYGUARD_GOING_AWAY: return openingAppHasWallpaper ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER : TRANSIT_OLD_KEYGUARD_GOING_AWAY; case TRANSIT_KEYGUARD_OCCLUDE: // When there is a closing app, the keyguard has already been occluded by an // activity, and another activity has started on top of that activity, so normal // app transition animation should be used. return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE : TRANSIT_OLD_ACTIVITY_OPEN; case TRANSIT_KEYGUARD_UNOCCLUDE: return TRANSIT_OLD_KEYGUARD_UNOCCLUDE; } // This is not keyguard transition and one of the app has request to skip app transition. // MIUI MOD: START // For Stage Split Screen: Drag to enter split-screen feature. // if (skipAppTransitionAnimation) { if (skipAppTransitionAnimation || ActivityTaskManagerServiceStub.get().removeSplitTaskShotIfNeed()) { // END return WindowManager.TRANSIT_OLD_UNSET; } @TransitionFlags final int flags = appTransition.getTransitFlags(); @TransitionType final int firstTransit = appTransition.getFirstAppTransition(); // Special transitions // TODO(new-app-transitions): Revisit if those can be rewritten by using flags. if (appTransition.containsTransitRequest(TRANSIT_CHANGE) && !changingContainers.isEmpty()) { // MIUI ADD: START Activity Embedding Resizing if (MiuiEmbeddingWindowServiceStub.get() .skipAppTransitionAnimationForEmbeddingDivider()) { return WindowManager.TRANSIT_OLD_UNSET; } // END @TransitContainerType int changingType = getTransitContainerType(changingContainers.valueAt(0)); switch (changingType) { case TYPE_TASK: return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; case TYPE_TASK_FRAGMENT: return TRANSIT_OLD_TASK_FRAGMENT_CHANGE; default: throw new IllegalStateException( "TRANSIT_CHANGE with unrecognized changing type=" + changingType); } } if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) { return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; } if (firstTransit == TRANSIT_NONE) { return TRANSIT_OLD_NONE; } /* * There are cases where we open/close a new task/activity, but in reality only a * translucent activity on top of existing activities is opening/closing. For that one, we * have a different animation because non of the task/activity animations actually work well * with translucent apps. */ if (isNormalTransit(firstTransit)) { boolean allOpeningVisible = true; boolean allTranslucentOpeningApps = !openingApps.isEmpty(); for (int i = openingApps.size() - 1; i >= 0; i--) { final ActivityRecord activity = openingApps.valueAt(i); if (!activity.isVisible()) { allOpeningVisible = false; if (activity.fillsParent()) { allTranslucentOpeningApps = false; } } } boolean allTranslucentClosingApps = !closingApps.isEmpty(); for (int i = closingApps.size() - 1; i >= 0; i--) { if (closingApps.valueAt(i).fillsParent()) { allTranslucentClosingApps = false; break; } } if (allTranslucentClosingApps && allOpeningVisible) { return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; } if (allTranslucentOpeningApps && closingApps.isEmpty()) { return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; } } final ActivityRecord topOpeningApp = getTopApp(openingApps, false /* ignoreHidden */); final ActivityRecord topClosingApp = getTopApp(closingApps, true /* ignoreHidden */); if (closingAppHasWallpaper && openingAppHasWallpaper) { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!"); switch (firstTransit) { case TRANSIT_OPEN: case TRANSIT_TO_FRONT: return TRANSIT_OLD_WALLPAPER_INTRA_OPEN; case TRANSIT_CLOSE: case TRANSIT_TO_BACK: return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; } } else if (oldWallpaper != null && !openingApps.isEmpty() && !openingApps.contains(oldWallpaper.mActivityRecord) && closingApps.contains(oldWallpaper.mActivityRecord) && topClosingApp == oldWallpaper.mActivityRecord) { // We are transitioning from an activity with a wallpaper to one without. return TRANSIT_OLD_WALLPAPER_CLOSE; } else if (wallpaperTarget != null && wallpaperTarget.isVisible() && openingApps.contains(wallpaperTarget.mActivityRecord) && topOpeningApp == wallpaperTarget.mActivityRecord /* && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE */) { // We are transitioning from an activity without // a wallpaper to now showing the wallpaper return TRANSIT_OLD_WALLPAPER_OPEN; } final ArraySet openingWcs = getAnimationTargets( openingApps, closingApps, true /* visible */); final ArraySet closingWcs = getAnimationTargets( openingApps, closingApps, false /* visible */); final WindowContainer openingContainer = !openingWcs.isEmpty() ? openingWcs.valueAt(0) : null; final WindowContainer closingContainer = !closingWcs.isEmpty() ? closingWcs.valueAt(0) : null; @TransitContainerType int openingType = getTransitContainerType(openingContainer); @TransitContainerType int closingType = getTransitContainerType(closingContainer); if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && openingType == TYPE_TASK) { return TRANSIT_OLD_TASK_TO_FRONT; } if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && closingType == TYPE_TASK) { return TRANSIT_OLD_TASK_TO_BACK; } if (appTransition.containsTransitRequest(TRANSIT_OPEN)) { if (openingType == TYPE_TASK) { return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0 ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN; } if (openingType == TYPE_ACTIVITY) { return TRANSIT_OLD_ACTIVITY_OPEN; } if (openingType == TYPE_TASK_FRAGMENT) { return TRANSIT_OLD_TASK_FRAGMENT_OPEN; } } if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) { if (closingType == TYPE_TASK) { return TRANSIT_OLD_TASK_CLOSE; } if (closingType == TYPE_TASK_FRAGMENT) { return TRANSIT_OLD_TASK_FRAGMENT_CLOSE; } if (closingType == TYPE_ACTIVITY) { for (int i = closingApps.size() - 1; i >= 0; i--) { if (closingApps.valueAt(i).visibleIgnoringKeyguard) { return TRANSIT_OLD_ACTIVITY_CLOSE; } } // Skip close activity transition since no closing app can be visible return WindowManager.TRANSIT_OLD_UNSET; } } if (appTransition.containsTransitRequest(TRANSIT_RELAUNCH) && !openingWcs.isEmpty() && !openingApps.isEmpty()) { return TRANSIT_OLD_ACTIVITY_RELAUNCH; } return TRANSIT_OLD_NONE; } 加载和开始动画
getAnimationAdapter加载动画
首先遍历所有的windowContainer 调用applyAnimation
private void applyAnimations(ArraySet wcs, ArraySet apps, @TransitionOldType int transit, boolean visible, LayoutParams animLp, boolean voiceInteraction) { final int wcsCount = wcs.size(); for (int i = 0; i 然后调用getAnimationAdapter 根据窗口层次结构中给定的窗口布局属性获取动画适配器
startAnimation 创建leash开始动画
SurfaceAnimation.java166 void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,167 @AnimationType int type,168 @Nullable OnAnimationFinishedCallback animationFinishedCallback,169 @Nullable Runnable animationCancelledCallback,170 @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {171 cancelAnimation(t, true /* restarting */, true /* forwardCancel */);172 mAnimation = anim;173 mAnimationType = type;174 mSurfaceAnimationFinishedCallback = animationFinishedCallback;175 mAnimationCancelledCallback = animationCancelledCallback;176 final SurfaceControl surface = mAnimatable.getSurfaceControl();177 if (surface == null) {178 Slog.w(TAG, "Unable to start animation, surface is null or no children.");179 cancelAnimation();180 return;181 }182 mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;183 if (mLeash == null) {184 mLeash = createAnimationLeash(mAnimatable, surface, t, type,185 mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,186 0 /* y */, hidden, mService.mTransactionFactory);187 mAnimatable.onAnimationLeashCreated(t, mLeash);188 }189 mAnimatable.onLeashAnimationStarting(t, mLeash);190 if (mAnimationStartDelayed) {191 ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable);192 return;193 }194 mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);195 if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {196 StringWriter sw = new StringWriter();197 PrintWriter pw = new PrintWriter(sw);198 mAnimation.dump(pw, "");199 ProtoLog.d(WM_DEBUG_ANIM, "Animation start for %s, anim=%s", mAnimatable, sw);200 }201 if (snapshotAnim != null) {202 mSnapshot = freezer.takeSnapshotForAnimation();203 if (mSnapshot == null) {204 Slog.e(TAG, "No snapshot target to start animation on for " + mAnimatable);205 return;206 }207 mSnapshot.startAnimation(t, snapshotAnim, type);208 }209 } 可见创建一个SuefaceControl, 然后把 WindowContainer的surface挂在了leash上面, 然后对leash做动画
455 static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,456 Transaction t, @AnimationType int type, int width, int height, int x, int y,457 boolean hidden, Supplier transactionFactory) {458 ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to leash for %s", animatable);459 final SurfaceControl.Builder builder = animatable.makeAnimationLeash()460 .setParent(animatable.getAnimationLeashParent())461 .setName(surface + " - animation-leash of " + animationTypeToString(type))462 // TODO(b/151665759) Defer reparent calls463 // We want the leash to be visible immediately because the transaction which shows464 // the leash may be deferred but the reparent will not. This will cause the leashed465 // surface to be invisible until the deferred transaction is applied. If this466 // doesn't work, you will can see the 2/3 button nav bar flicker during seamless467 // rotation.468 .setHidden(hidden)469 .setEffectLayer()470 .setCallsite("SurfaceAnimator.createAnimationLeash");471 final SurfaceControl leash = builder.build();472 t.setWindowCrop(leash, width, height);473 t.setPosition(leash, x, y);474 t.show(leash);475 t.setAlpha(leash, hidden ? 0 : 1);476 477 t.reparent(surface, leash);478 return leash;479 }
SurfaceAnimationRunner.java171 void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,172 Runnable finishCallback) {173 synchronized (mLock) {174 final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,175 finishCallback);176 boolean requiresEdgeExtension = requiresEdgeExtension(a);177 178 if (requiresEdgeExtension) {179 final ArrayList extensionSurfaces = new ArrayList();180 synchronized (mEdgeExtensionLock) {181 mEdgeExtensions.put(animationLeash, extensionSurfaces);182 }183 184 mPreProcessingAnimations.put(animationLeash, runningAnim);185 186 // We must wait for t to be committed since otherwise the leash doesn't have the187 // windows we want to screenshot and extend as children.188 t.addTransactionCommittedListener(mEdgeExtensionExecutor, () -> {189 final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec();190 191 final Transaction edgeExtensionCreationTransaction = new Transaction();192 edgeExtendWindow(animationLeash,193 animationSpec.getRootTaskBounds(), animationSpec.getAnimation(),194 edgeExtensionCreationTransaction);195 196 synchronized (mLock) {197 // only run if animation is not yet canceled by this point198 if (mPreProcessingAnimations.get(animationLeash) == runningAnim) {199 // In the case the animation is cancelled, edge extensions are removed200 // onAnimationLeashLost which is called before onAnimationCancelled.201 // So we need to check if the edge extensions have already been removed202 // or not, and if so we don't want to apply the transaction.203 synchronized (mEdgeExtensionLock) {204 if (!mEdgeExtensions.isEmpty()) {205 edgeExtensionCreationTransaction.apply();206 }207 }208 209 mPreProcessingAnimations.remove(animationLeash);210 mPendingAnimations.put(animationLeash, runningAnim);211 if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {212 mChoreographer.postFrameCallback(this::startAnimations);213 }214 }215 }216 });217 }218 219 if (!requiresEdgeExtension) {220 mPendingAnimations.put(animationLeash, runningAnim);221 if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {222 mChoreographer.postFrameCallback(this::startAnimations);223 }224 }225 226 // Some animations (e.g. move animations) require the initial transform to be227 // applied immediately.228 applyTransformation(runningAnim, t, 0 /* currentPlayTime */);229 }230 } 发送消息
scheduleFrameLocked:862, Choreographer (android.view)postCallbackDelayedInternal:605, Choreographer (android.view)postFrameCallbackDelayed:702, Choreographer (android.view)postFrameCallback:682, Choreographer (android.view)continueStartingAnimations:173, SurfaceAnimationRunner (com.android.server.wm)handleAppTransitionReady:312, AppTransitionController (com.android.server.wm)checkAppTransitionReady:1070, RootWindowContainer (com.android.server.wm)performSurfacePlacementNoTrace:937, RootWindowContainer (com.android.server.wm)performSurfacePlacement:877, RootWindowContainer (com.android.server.wm)performSurfacePlacementLoop:199, WindowSurfacePlacer (com.android.server.wm)performSurfacePlacement:148, WindowSurfacePlacer (com.android.server.wm)performSurfacePlacement:137, WindowSurfacePlacer (com.android.server.wm)handleAppTransitionTimeout:1814, AppTransition (com.android.server.wm)lambda$new为动画添加listener
$com-android-server-wm-AppTransition:243, AppTransition (com.android.server.wm)run:-1, AppTransition$$ExternalSyntheticLambda3 (com.android.server.wm)handleCallback:942, Handler (android.os)dispatchMessage:99, Handler (android.os)loopOnce:211, Looper (android.os)loop:300, Looper (android.os)run:67, HandlerThread (android.os)run:46, ServiceThread (com.android.server)
SurfaceAnimationRunner.java private void startAnimationLocked(RunningAnimation a) {270 final ValueAnimator anim = mAnimatorFactory.makeAnimator();271 272 // Animation length is already expected to be scaled.273 anim.overrideDurationScale(1.0f);274 anim.setDuration(a.mAnimSpec.getDuration());275 anim.addUpdateListener(animation -> {276 synchronized (mCancelLock) {277 if (!a.mCancelled) {278 final long duration = anim.getDuration();279 long currentPlayTime = anim.getCurrentPlayTime();280 if (currentPlayTime > duration) {281 currentPlayTime = duration;282 }283 applyTransformation(a, mFrameTransaction, currentPlayTime);284 }285 }286 287 // Transaction will be applied in the commit phase.288 scheduleApplyTransaction();289 });290 291 anim.addListener(new AnimatorListenerAdapter() {292 @Override293 public void onAnimationStart(Animator animation) {294 synchronized (mCancelLock) {295 if (!a.mCancelled) {296 // TODO: change this back to use show instead of alpha when b/138459974 is297 // fixed.298 mFrameTransaction.setAlpha(a.mLeash, 1);299 }300 }301 }302 303 @Override304 public void onAnimationEnd(Animator animation) {305 synchronized (mLock) {306 mRunningAnimations.remove(a.mLeash);307 synchronized (mCancelLock) {308 if (!a.mCancelled) {309 310 // Post on other thread that we can push final state without jank.311 mAnimationThreadHandler.post(a.mFinishCallback);312 }313 }314 }315 }316 });317 a.mAnim = anim;318 mRunningAnimations.put(a.mLeash, a);319 320 anim.start();321 if (a.mAnimSpec.canSkipFirstFrame()) {322 // If we can skip the first frame, we start one frame later.323 anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS);324 }325 326 // Immediately start the animation by manually applying an animation frame. Otherwise, the327 // start time would only be set in the next frame, leading to a delay.328 anim.doAnimationFrame(mChoreographer.getFrameTime());329 }330 doFrame时更新动画位置
private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) { a.mAnimSpec.apply(t, a.mLeash, currentPlayTime); } 放vsync到来后, 执行onAnimationUpdate
119 @Override120 public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {121 final TmpValues tmp = mThreadLocalTmps.get();122 tmp.transformation.clear();123 mAnimation.getTransformation(currentPlayTime, tmp.transformation);124 tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);125 t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);126 t.setAlpha(leash, tmp.transformation.getAlpha());127 128 boolean cropSet = false;129 if (mRootTaskClipMode == ROOT_TASK_CLIP_NONE) {130 if (tmp.transformation.hasClipRect()) {131 final Rect clipRect = tmp.transformation.getClipRect();132 accountForExtension(tmp.transformation, clipRect);133 t.setWindowCrop(leash, clipRect);134 cropSet = true;135 }136 } else {137 mTmpRect.set(mRootTaskBounds);138 if (tmp.transformation.hasClipRect()) {139 mTmpRect.intersect(tmp.transformation.getClipRect());140 }141 accountForExtension(tmp.transformation, mTmpRect);142 t.setWindowCrop(leash, mTmpRect);143 cropSet = true;144 }145 146 // We can only apply rounded corner if a crop is set, as otherwise the value is meaningless,147 // since it doesn't have anything it's relative to.148 if (cropSet && mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) {149 t.setCornerRadius(leash, mWindowCornerRadius);150 }151 } 再来看下apply方法的具体实现,通过之前以具体xml资源创建的mAnimation对象,根据当前时间片currentPlayTime获取到当前的tmp.transformation,对leash对象实现了Matrix(大小,位置),Alpha,Crop等transformation变化,再通过Transaction 交给surfaceflinger显示,从而实现了动画当前时间片的显示效果。对比旧动画机制,这个transformation变化是在WindowStateAnimator类里面实现的。为什么要重点关注这个方法呢?因为如果窗口动画出bug了(位置大小不对?透明度异常?),就可以在这个方法里面打印window的相关参数来初步定位原因。
Rotation动画 ANIMATION_TYPE_SCREEN_ROTATION
6、ValueAnimator类,从上面的介绍可以得知,窗口动画的最终本质就是一个ValueAnimator属性动画,理解了这一点,就相当于把窗口动画简单化了,最终的实现就类比于我们普通app的属性动画的实现(app属性动画的对象是view,窗口属性动画的对象是window),只不过整个流程比较复杂而已,但是最终的实现原理是一样的,殊途同归,这个才是android窗口动画机制的精髓所在
FadeAnimationController.java public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) { if (windowToken == null || windowToken.getParent() == null) { return; } final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation(); final FadeAnimationAdapter animationAdapter = animation != null ? createAdapter(createAnimationSpec(animation), show, windowToken) : null; if (animationAdapter == null) { return; } windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter, show /* hidden */, animationType, null /* finishedCallback */); } 1. 根据sensor导致的旋转动画
最终也会到SurfaceAnimation
拿到动画适配器:
上一篇:超聚变存储服务器怎么用?
下一篇:王者怎么换服务器失败状态?