过渡动画总结
本文主要是基于android S版本来整理下过渡动画的初始化过程以及启动过程,过渡动画是指在Activity切换过程中或者是从桌面打开app过程中出现的切换动画。首先,看下目前android存在的几种动画,这里直接参考了源远大佬的图
目标WindowContainer | 名称 | 示例 |
---|---|---|
WindowState | 窗口动画 | Toast弹出动画、dialog弹出动画 |
AppWindowToken | 过渡动画 | Activity切换动画、app启动动画 |
Task | Task动画 | Recents动画 |
DisplayContent | 全屏动画 | 旋转屏动画 |
本文的主要目录结构如下:
1.Activity的切换过程
Activity的切换过程主要就是指之前的activity从resumed状态-> paused状态,新打开的activity从start状态->resume状态,并将前一个窗口设置为不可见,新打开的窗口设置为可见,而过渡动画的初始化也是在Activity切换过程中完成的。
我们先来跟一下Activity的切换过程,然后会在该过程中去preareApptransition。
Android在S版本上取消了ActivityStack的定义,而是采用了更高级的抽象Task直接来代替stack(区分时可以通过asTask方法进行判断),resume新的Activity会经历如下流程:
Task#resumeTopActivityUncheckedLocked -> Task#resumeTopActivityInnerLocked -> DisplayContent#prepareApptransition -> ... -> apply过渡动画
1.1 Task#resumeTopActivityUncheckedLocked
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options,
boolean deferPause) {
if (mInResumeTopActivity) { //已经resume 直接退出
// Don't even start recursing.
return false;
}
boolean someActivityResumed = false;
try {
// Protect against recursion.
mInResumeTopActivity = true;
if (isLeafTask()) { //就是判断是ActivityRecord还是task 不是task则进入
if (isFocusableAndVisible()) { //判断是否是顶部focus的Activity以及其是否应该可见
someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause); //继续resume流程
}
} else {
//如果当前是task 则获取其child开始resume流程
int idx = mChildren.size() - 1;
while (idx >= 0) {
final Task child = getChildAt(idx--).asTask();
if (child == null) {
continue;
}
// END
if (!child.isTopActivityFocusable()) {
continue;
}
if (child.getVisibility(null /* starting */) != TASK_VISIBILITY_VISIBLE) {
break;
}
someActivityResumed |= child.resumeTopActivityUncheckedLocked(prev, options,
deferPause);
// Doing so in order to prevent IndexOOB since hierarchy might changes while
// resuming activities, for example dismissing split-screen while starting
// non-resizeable activity.
if (idx >= mChildren.size()) {
idx = mChildren.size() - 1;
}
}
}
// When resuming the top activity, it may be necessary to pause the top activity (for
// example, returning to the lock screen. We suppress the normal pause logic in
// {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the
// end. We call the {@link ActivityTaskSupervisor#checkReadyForSleepLocked} again here
// to ensure any necessary pause logic occurs. In the case where the Activity will be
// shown regardless of the lock screen, the call to
// {@link ActivityTaskSupervisor#checkReadyForSleepLocked} is skipped.
final ActivityRecord next = topRunningActivity(true /* focusableOnly */);
if (next == null || !next.canTurnScreenOn()) {
checkReadyForSleep();
}
} finally {
mInResumeTopActivity = false;
}
return someActivityResumed;
}
1.2 Task#resumeTopActivityInnerLocked
在Activity切换过程中会触发两次resumeTopActivityInnerLocked,第一次是在startActivity过程中,此时之前的Activity 仍未paused,因此prev不等于null,所以在判断isPausing时返回true,然后直接返回;
而第二次是在activityPaused触发的,此时之前的activity已经pause了,所以prev为null,会进入后续的prepareAppTransition流程
Task#resumeTopActivityInnerLocked
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options,
boolean deferPause) {
.....
// We are starting up the next activity, so tell the window manager
// that the previous one will be hidden soon. This way it can know
// to ignore it when computing the desired screen orientation.
boolean anim = true;
final DisplayContent dc = taskDisplayArea.mDisplayContent;
if (prev != null) { //prev 为null
if (prev.finishing) {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare close transition: prev=" + prev);
if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
anim = false;
dc.prepareAppTransition(TRANSIT_NONE);
} else {
dc.prepareAppTransition(TRANSIT_CLOSE);
}
prev.setVisibility(false);
} else {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare open transition: prev=" + prev);
if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
dc.prepareAppTransition(TRANSIT_NONE);
} else {
dc.prepareAppTransition(TRANSIT_OPEN,
next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
}
}
} else { //进入该分支
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
dc.prepareAppTransition(TRANSIT_NONE);
} else {
dc.prepareAppTransition(TRANSIT_OPEN); //准备start过渡动画
}
}
if (anim) {
next.applyOptionsAnimation();
} else {
next.abortAndClearOptionsAnimation();
}
}
1.2.1 DisplayContent#prepareAppTransition、AppTransition#prepareAppTransition
void prepareAppTransition(@WindowManager.TransitionType int transit,
@WindowManager.TransitionFlags int flags) {
final boolean prepared = mAppTransition.prepareAppTransition(transit, flags); //继续进入AppTransition的prepareAppTransition
if (prepared && okToAnimate()) {
mSkipAppTransitionAnimation = false;
}
}
boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) {
if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
return false;
}
mNextAppTransitionRequests.add(transit);
mNextAppTransitionFlags |= flags;
updateBooster();
removeAppTransitionTimeoutCallbacks();
mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable,
APP_TRANSITION_TIMEOUT_MS);
return prepare();
}
prepareAppTransition方法中,S版本和R版本还是有一些不同的,R版本是在该方法内通过设置mNextAppTransition来指定下一次transition的类型
,在S上是通过mNextAppTransitionRequests来保存下一次transit的类型,具体会在后面handleAppTransitionReady方法内通过getTransitCompatType方法来获取当前transition的类型</font>
(见2.3.2)
appTransition的准备过程如下:
(Activity的创建会在create的时候会通过setContentView方法将当前view添加RootView上,而在addView方法内会去创建窗口并将当前rootView添加到该窗口上)
在新Activity resume过程中会去添加当前窗口(addView的时候实现),然后继续走measure、layout、draw的流程,在这一系列完成之后会调用wms.finishDrawingWindow,然后就开始调用WindowSurfacePlacer#requestTraversal方法了
2.AppTransition动画的触发过程
2.1 RootWindowContainer#performSurfacePlacementNoTrace
app的transition过程都会从RootWindowContainer#performSurfacePlacementNoTrace方法内开始调用
handleAppTransitionReady:160, AppTransitionController (com.android.server.wm)
checkAppTransitionReady:1044, RootWindowContainer (com.android.server.wm)
performSurfacePlacementNoTrace:879, RootWindowContainer (com.android.server.wm)
performSurfacePlacement:813, RootWindowContainer (com.android.server.wm)
performSurfacePlacementLoop:199, WindowSurfacePlacer (com.android.server.wm)
performSurfacePlacement:148, WindowSurfacePlacer (com.android.server.wm)
relayoutWindow:2675, WindowManagerService (com.android.server.wm)
relayout:241, Session (com.android.server.wm) //或者是activityPaused方法的触发
onTransact:747, IWindowSession$Stub (android.view)
onTransact:171, Session (com.android.server.wm)
execTransactInternal:1187, Binder (android.os)
execTransact:1146, Binder (android.os)
performSurfacePlacementNoTrace方法源头都是触发窗口重新绘制,然后再去设置activity或者窗口的transition过程(窗口动画的入口也是performSurfacePlacementNoTrace)
我们来看下该方法 RootWindowContainer#performSurfacePlacementNoTrace
void performSurfacePlacementNoTrace() {
int i;
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
// Initialize state of exiting tokens.
final int numDisplays = mChildren.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
displayContent.setExitingTokensHasVisible(false);
}
mHoldScreen = null;
mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mUserActivityTimeout = -1;
mObscureApplicationContentOnSecondaryDisplays = false;
mSustainedPerformanceModeCurrent = false;
mWmService.mTransactionSequence++;
// TODO(multi-display): recents animation & wallpaper need support multi-display.
final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction(); //更新surface变化
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
}
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mSyncEngine.onSurfacePlacement();
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer); //开始transition动画的设置
2.2 RootWindowContainer#checkAppTransitionReady
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
// Trace all displays app transition by Z-order for pending layout change.
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent curDisplay = mChildren.get(i);
//check当前display的apptransition是否是准备好的状态(dc会通过setReady方法设置该状态)
if (curDisplay.mAppTransition.isReady()) {
// handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.
//处理app transition
curDisplay.mAppTransitionController.handleAppTransitionReady();
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady",
curDisplay.pendingLayoutChanges);
}
}
2.3 ApptransitionController#handleAppTransitionReady
这里是对transition动画的一些初始化工作。
void handleAppTransitionReady() {
mTempTransitionReasons.clear();
//本次transition过程是否已准备好 过程见2.3.1
if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
|| !transitionGoodToGo(mDisplayContent.mChangingContainers,
mTempTransitionReasons)) {
return; //未准备好 返回
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
//获取dc之前准备好的transition
final AppTransition appTransition = mDisplayContent.mAppTransition;
mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
appTransition.removeAppTransitionTimeoutCallbacks();
mDisplayContent.mWallpaperMayChange = false;
int appCount = mDisplayContent.mOpeningApps.size();
for (int i = 0; i < appCount; ++i) {
// 这里先直译备注下:具体还不太了解 后续再跟下这块逻辑
// 在进入动画之前清除 mAnimatingExit这个flag
// 这个flag会在窗口被移除或者relayout变为不可见是被设置为true
// 这个flag同样会影响窗口的可见性
// 我们需要在maybeUpdateTransitToWallpaper方法前清楚这个flag,因为transition的选择取决于wallpaper target的可见性
mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
}
appCount = mDisplayContent.mChangingContainers.size();
for (int i = 0; i < appCount; ++i) {
// Clearing for same reason as above.
final ActivityRecord activity = getAppFromContainer(
mDisplayContent.mChangingContainers.valueAtUnchecked(i));
if (activity != null) {
activity.clearAnimatingFlags();
}
}
// Adjust wallpaper before we pull the lower/upper target, since pending changes
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
mDisplayContent.mOpeningApps);
//S版本重新定义了transition的类型 这里需要获取下兼容后的type 详细见2.3.2
final @TransitionOldType int transit = getTransitCompatType(
mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
mDisplayContent.mSkipAppTransitionAnimation);
mDisplayContent.mSkipAppTransitionAnimation = false;
//当我们打开一个具有动画的activity时 这里的transit就是 TRANSIT_OLD_ACTIVITY_OPEN
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"handleAppTransitionReady: displayId=%d appTransition={%s}"
+ " openingApps=[%s] closingApps=[%s] transit=%s",
mDisplayContent.mDisplayId,
appTransition.toString(),
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
AppTransition.appTransitionOldToString(transit));
// Find the layout params of the top-most application window in the tokens, which is
// what will control the animation theme. If all closing windows are obscured, then there is
// no need to do an animation. This is the case, for example, when this transition is being
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes);
//当前要打开的app
final ActivityRecord topOpeningApp =
getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
//当前要close的app
final ActivityRecord topClosingApp =
getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
final ActivityRecord topChangingApp =
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
//根据该Activity 窗口的attrs获取动画参数
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
//如果存在RemoteAnimationAdapter(App端可能会定义) 那么覆盖这个transition
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
|| containsVoiceInteraction(mDisplayContent.mOpeningApps);
final int layoutRedo;
//defer start动画
mService.mSurfaceAnimationRunner.deferStartingAnimations();
try {
//创建启动动画 见2.4
applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
animLp, voiceInteraction);
handleClosingApps();
handleOpeningApps();
handleChangingApps(transit);
appTransition.setLastAppTransition(transit, topOpeningApp,
topClosingApp, topChangingApp);
final int flags = appTransition.getTransitFlags();
layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
handleNonAppWindowsInTransition(transit, flags);
appTransition.postAnimationCallback();
appTransition.clear();
} finally {
//这里继续开始start动画
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
//回调通知TaskSnapshotController对close的app进行snapshot截屏
mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
//一些清理工作
mDisplayContent.mOpeningApps.clear();
mDisplayContent.mClosingApps.clear();
mDisplayContent.mChangingContainers.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
mDisplayContent.setLayoutNeeded();
//transition执行完之后 需要重新计算当前的ime ttarget
mDisplayContent.computeImeTarget(true /* updateImeTarget */);
mService.mAtmService.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
mTempTransitionReasons);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
mDisplayContent.pendingLayoutChanges |=
layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
}
2.3.1 ApptransitionController#transitionGoodToGo
private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
ArrayMap<WindowContainer, Integer> outReasons) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent(
Display.DEFAULT_DISPLAY).getRotationAnimation();
//如果动画没有设置暂停
if (!mDisplayContent.mAppTransition.isTimeout()) {
//一种特殊情况:如果当前正在做屏幕旋转动画 同时当前确实有待转屏的变化
//当前返回false ,defer 本次transition
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()
&& mDisplayContent.getDisplayRotation().needsUpdate()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Delaying app transition for screen rotation animation to finish");
return false;
}
for (int i = 0; i < apps.size(); i++) {
WindowContainer wc = apps.valueAt(i);
final ActivityRecord activity = getAppFromContainer(wc);
if (activity == null) {
continue;
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ "startingMoved=%b isRelaunching()=%b startingWindow=%s",
activity, activity.allDrawn, activity.startingDisplayed,
activity.startingMoved, activity.isRelaunching(),
activity.mStartingWindow);
final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
return false;
}
if (allDrawn) {
outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN);
} else {
outReasons.put(activity,
activity.mStartingData instanceof SplashScreenStartingData
? APP_TRANSITION_SPLASH_SCREEN
: APP_TRANSITION_SNAPSHOT);
}
}
//其他一些原因 有兴趣的可以继续跟下 :)
// We also need to wait for the specs to be fetched, if needed.
if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "isFetchingAppTransitionSpecs=true");
return false;
}
if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "unknownApps is not empty: %s",
mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
return false;
}
// If the wallpaper is visible, we need to check it's ready too.
boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
mWallpaperControllerLocked.wallpaperTransitionReady();
if (wallpaperReady) {
return true;
}
return false;
}
return true;
}
2.3.2 ApptransitionController#getTransitCompatType
S上重构了Transition的相关逻辑
,但是S上还是不完全的,因为他仍然要通过getTransitCompatType将新的transtionType转换为之前的TransitionOldType,再根据TransitionOldType来确定当前的transition类别
,应该在T版本会有完全重构的逻辑了。
新旧type对应关系如下:
之前的TRANSIT_ACTIVITY_OPEN 就变成了 TRANSIT_OLD_ACTIVITY_OPEN
static @TransitionOldType int getTransitCompatType(AppTransition appTransition,
ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
@Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper,
boolean skipAppTransitionAnimation) {
//appTransition的type是根据之前1.2.1在prepareAppTransition方法设置的
//mNextAppTransitionRequests来确定当前类别,这个列表存储了已请求的appTransition
// Determine if closing and opening app token sets are wallpaper targets, in which case
// special animations are needed.
//确定当前发生动画的是否是wallpaper target,在这种情况下需要特殊的动画
final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps)
&& wallpaperTarget != null;
final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps)
&& wallpaperTarget != null;
//如果当前transition中有锁屏动画的 那么直接返回对应的动画
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;
}
// dc内是否设置了 skipAppTransitionAnimation(app可能会请求跳过transition动画)
if (skipAppTransitionAnimation) {
return WindowManager.TRANSIT_OLD_UNSET;
}
final @TransitionFlags int flags = appTransition.getTransitFlags();
final @TransitionType int firstTransit = appTransition.getFirstAppTransition();
//处理特殊transition
if (appTransition.containsTransitRequest(TRANSIT_CHANGE)) {
return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
}
if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) {
return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
}
if (firstTransit == TRANSIT_NONE) {
return TRANSIT_OLD_NONE;
}
//实际上对一个位于已存在activities顶部的半透明activity,我们会选择使用不同的动画
//因为没有task/activity动画能够很好处理这种半透明app
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;
}
}
//获取即将 要打开的app
final ActivityRecord topOpeningApp = getTopApp(openingApps,
false /* ignoreHidden */);
//即将要关闭的app
final ActivityRecord topClosingApp = getTopApp(closingApps,
true /* ignoreHidden */);
//如果是wallpaper target 返回wallpaper相关动画 下面也是处理wallpaperTarget相关场景
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;
}
//处理普通task/Activity场景 根据task/Activity 以及open/close选择对应的动画
final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
openingApps, closingApps, true /* visible */);
final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
openingApps, closingApps, false /* visible */);
final boolean isActivityOpening = !openingWcs.isEmpty()
&& openingWcs.valueAt(0).asActivityRecord() != null;
final boolean isActivityClosing = !closingWcs.isEmpty()
&& closingWcs.valueAt(0).asActivityRecord() != null;
final boolean isTaskOpening = !openingWcs.isEmpty() && !isActivityOpening;
final boolean isTaskClosing = !closingWcs.isEmpty() && !isActivityClosing;
//task进入前台
if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && isTaskOpening) {
return TRANSIT_OLD_TASK_TO_FRONT;
}
//task进入后台
if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && isTaskClosing) {
return TRANSIT_OLD_TASK_TO_BACK;
}
if (appTransition.containsTransitRequest(TRANSIT_OPEN)) {
if (isTaskOpening) {
return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0
? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN;
//打开task动画
}
if (isActivityOpening) {
//打开activity动画
return TRANSIT_OLD_ACTIVITY_OPEN;
}
}
if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) {
if (isTaskClosing) {
//关闭task动画
return TRANSIT_OLD_TASK_CLOSE;
}
if (isActivityClosing) {
for (int i = closingApps.size() - 1; i >= 0; i--) {
if (closingApps.valueAt(i).visibleIgnoringKeyguard) {
//关闭activity动画
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;
}
2.4 ApptransitionController#applyAnimations
private void applyAnimations(ArraySet<ActivityRecord> openingApps,
ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit,
LayoutParams animLp, boolean voiceInteraction) {
if (transit == WindowManager.TRANSIT_OLD_UNSET
|| (openingApps.isEmpty() && closingApps.isEmpty())) {
return;
}
final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
openingApps, closingApps, true /* visible */);
final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
openingApps, closingApps, false /* visible */);
//对对应的窗口容器应用对应的transition动画 见2.5
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
for (int i = 0; i < openingApps.size(); ++i) {
openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
}
for (int i = 0; i < closingApps.size(); ++i) {
closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
}
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
if (accessibilityController != null) {
accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
}
}
2.5 ApptransitionController#applyAnimations
/**
* Apply animation to the set of window containers.
*
* @param wcs The list of {@link WindowContainer}s to which an app transition animation applies.
* @param apps The list of {@link ActivityRecord}s being transitioning.
* @param transit The current transition type.
* @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes
* invisible.
* @param animLp Layout parameters in which an app transition animation runs.
* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
* interaction session driving task.
*/
private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,
@TransitionOldType int transit, boolean visible, LayoutParams animLp,
boolean voiceInteraction) {
final int wcsCount = wcs.size();
for (int i = 0; i < wcsCount; i++) {
final WindowContainer wc = wcs.valueAt(i);
// If app transition animation target is promoted to higher level, SurfaceAnimator
// triggers WC#onAnimationFinished only on the promoted target. So we need to take care
// of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the
// app transition.
final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
for (int j = 0; j < apps.size(); ++j) {
final ActivityRecord app = apps.valueAt(j);
if (app.isDescendantOf(wc)) {
transitioningDescendants.add(app);
}
}
//这里wc是ActivityRecord 最终会调用其父类WindowContainer的applyAnimation
wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
}
}
2.6 WindowContainer#applyAnimation、applyAnimationUnchecked
boolean applyAnimation(WindowManager.LayoutParams lp, @TransitionOldType int transit,
boolean enter, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
if (mWmService.mDisableTransitionAnimation) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: transition animation is disabled or skipped. "
+ "container=%s", this);
cancelAnimation();
return false;
}
//只有当屏幕没有被冻结的时候才能做动画 否则会产生奇怪的现象
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
if (okToAnimate()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: transit=%s, enter=%b, wc=%s",
AppTransition.appTransitionOldToString(transit), enter, this);
//继续调用
applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
} else {
cancelAnimation();
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
return isAnimating();
}
WindowContainer#applyAnimationUnchecked
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
@TransitionOldType int transit, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
final Task task = asTask(); //如果即将打开的容器是activity 那么这里就为null
//task不为null 且是close当前task 同时该task不是home以及最近任务task
//那么会根据ime相关属性 显示输入法的screenShot (S新增特性,优化输入法transition的体验)
if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
&& imeTarget.getWindow().getTask() == task;
//当前是在closetask同时当前imetarget不为null
if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {
mDisplayContent.showImeScreenshot();
}
}
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
transit, enter, isVoiceInteraction);
AnimationAdapter adapter = adapters.first;
AnimationAdapter thumbnailAdapter = adapters.second;
if (adapter != null) {
if (sources != null) {
mSurfaceAnimationSources.addAll(sources);
}
//执行动画 见2.7
startAnimation(getPendingTransaction(), adapter, !isVisible(),
ANIMATION_TYPE_APP_TRANSITION);
if (adapter.getShowWallpaper()) {
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
if (thumbnailAdapter != null) {
mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),
thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> { });
}
}
}
2.6.1 WindowContainer#getAnimationAdapter
窗口动画的显示过程中同样会加载到这里,根据窗口的layoutParams加载动画
Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
@TransitionOldType int transit, boolean enter, boolean isVoiceInteraction) {
final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
final int appRootTaskClipMode = getDisplayContent().mAppTransition.getAppRootTaskClipMode();
// Separate position and size for use in animators.
final Rect screenBounds = getAnimationBounds(appRootTaskClipMode);
mTmpRect.set(screenBounds);
getAnimationPosition(mTmpPoint);
mTmpRect.offsetTo(0, 0);
// 如果当前存在RemoteAnimationAdapter 这里controller就不为null
final RemoteAnimationController controller =
getDisplayContent().mAppTransition.getRemoteAnimationController();
//判断apptransition是否发生变化
final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
&& isChangingAppTransition();
//RemoteAnimation情况
if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
final Rect localBounds = new Rect(mTmpRect);
localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
final RemoteAnimationController.RemoteAnimationRecord adapters =
controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds,
screenBounds, (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
//apptransition发生变化情况
} else if (isChanging) {
final float durationScale = mWmService.getTransitionAnimationScaleLocked();
final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowChangeAnimationSpec(mSurfaceFreezer.mFreezeBounds, mTmpRect,
displayInfo, durationScale, true /* isAppAnimation */,
false /* isThumbnail */),
getSurfaceAnimationRunner());
final AnimationAdapter thumbnailAdapter = mSurfaceFreezer.mSnapshot != null
? new LocalAnimationAdapter(new WindowChangeAnimationSpec(
mSurfaceFreezer.mFreezeBounds, mTmpRect, displayInfo, durationScale,
true /* isAppAnimation */, true /* isThumbnail */), getSurfaceAnimationRunner())
: null;
resultAdapters = new Pair<>(adapter, thumbnailAdapter);
mTransit = transit;
mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
} else {
//普通transition进入这里
mNeedsAnimationBoundsLayer = (appRootTaskClipMode == ROOT_TASK_CLIP_AFTER_ANIM);
//根据窗口的layoutParams加载出动画
final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
if (a != null) {
float windowCornerRadius = !inMultiWindowMode()
? getDisplayContent().getWindowCornerRadius()
: 0;
//将动画的相关信息封装到WindowAnimationSpec类内
WindowAnimationSpec spec = new WindowAnimationSpec(
a, mTmpPoint, mTmpRect,
getDisplayContent().mAppTransition.canSkipFirstFrame(),
appRootTaskClipMode,
true /* isAppAnimation */,
windowCornerRadius);
//创建AnimationAdapter 后续会调用其startAniamtion方法
//用来执行显示动画、回调动画取消等操作
AnimationAdapter adapter = new LocalAnimationAdapter(spec,
getSurfaceAnimationRunner());
spec.mShouldActivityTransitionRoundCorner =
getDisplayContent().mAppTransition.
shouldActivityTransitionRoundCorner();
if(this.asActivityRecord() != null) {
this.asActivityRecord().mWindowAnimationSpec = spec;
}
resultAdapters = new Pair<>(adapter, null);
mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP
|| AppTransition.isClosingTransitOld(transit);
mTransit = transit;
mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
} else {
resultAdapters = new Pair<>(null, null);
}
}
return resultAdapters; //返回adapter
}
2.7 WindowContainer#startAnimation、SurfaceAnimator#startAnimation
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback) {
if (DEBUG_ANIM) {
Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + 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,
mSurfaceFreezer);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable SurfaceFreezer freezer) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
mAnimationType = type;
mAnimationFinishedCallback = animationFinishedCallback;
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;
if (mLeash == null) {
//创建leash
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) {
if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed");
return;
}
//调用LocalAnimationAdaper的startAnimation(如果不存在RemoteAnimationAdapter)
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
}
该方法内会根据当前动画创建leash(译为:绳子,也就是捆绑了一组surface)
,最后执行LocalAnimationAdaper的startAnimation方法(假如app没有指定RemoteAnimationAdapter,例如桌面会设置)
2.8 LocalAnimationAdaper#startAnimation
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
() -> finishCallback.onAnimationFinished(type, this));
}
这里的mAnimator是SurfaceAnimationRunner类,继续看下SurfaceAnimationRunner#startAnimation方法
2.9 SurfaceAnimationRunner#startAnimation
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
Runnable finishCallback) {
synchronized (mLock) {
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
finishCallback);
mPendingAnimations.put(animationLeash, runningAnim);
//该值是在2.3 applyAnimation方法前通过deferStartingAnimations设置的
if (!mAnimationStartDeferred) {
mChoreographer.postFrameCallback(this::startAnimations);
}
//对一些动画立即进行初始变换
applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
}
}
首先这里根据描述动画的AnimationSpec类、leash、动画的callback构造了一个RunningAnimation
然后将这个animation以及leash添加到待显示的动画列表mPendingAnimations
内,然后该动画会在下一帧显示前通过回调被启动。
接下来就是判断mAnimationStartDeferred值,该值是在2.3的handleAppTransitionReady方法内设置的,他在applyAnimation方法前调用了deferStartingAnimations方法(设为true),然后在执行完applyAnimations方法后在finally块内调用了continueStartingAnimations(设为false)。
这里在apply动画之前进行defer操作是为了统一当doframe回调的时候才去执行动画。
那么Choreographer内的开启动画的callback是在哪里设置进去的呢?
其实还是在上述continueStartingAnimations方法内进行调用的,可以看到当动画添加到mPendingAnimations之后,在该方法内会将SufaceAnimationRunner的startAnimations方法作为callback添加到演舞者Choreographer内。
void continueStartingAnimations() {
synchronized (mLock) {
mAnimationStartDeferred = false;
if (!mPendingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
}
}
到这里的堆栈调用情况如下:
startAnimation:145, SurfaceAnimationRunner (com.android.server.wm)
startAnimation:55, LocalAnimationAdapter (com.android.server.wm)
startAnimation:161, SurfaceAnimator (com.android.server.wm)
startAnimation:2565, WindowContainer (com.android.server.wm)
startAnimation:2571, WindowContainer (com.android.server.wm)
applyAnimationUnchecked:2830, WindowContainer (com.android.server.wm)
applyAnimation:2669, WindowContainer (com.android.server.wm)
applyAnimation:5230, ActivityRecord (com.android.server.wm)
applyAnimations:598, AppTransitionController (com.android.server.wm)
applyAnimations:757, AppTransitionController (com.android.server.wm)
handleAppTransitionReady:233, AppTransitionController (com.android.server.wm)
checkAppTransitionReady:1044, RootWindowContainer (com.android.server.wm)
performSurfacePlacementNoTrace:879, RootWindowContainer (com.android.server.wm)
performSurfacePlacement:813, 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)
run:79, WindowSurfacePlacer$Traverser (com.android.server.wm)
handleCallback:938, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:210, Looper (android.os)
loop:299, Looper (android.os)
run:67, HandlerThread (android.os)
run:46, ServiceThread (com.android.server)
2.10 SurfaceAnimationRunner#startAnimations
当新的一帧正在被渲染的时候会回调到Choreographer的doCallback方法,最终会回调到之前SurfaceAnimationRunner添加到Choreographer的startAniamtions方法的回调,具体堆栈如下:
startPendingAnimationsLocked:191, SurfaceAnimationRunner (com.android.server.wm)
startAnimations:265, SurfaceAnimationRunner (com.android.server.wm)
$r8$lambda$u1Jh9N5fY2HKNOPRKT57txOp8-s:-1, SurfaceAnimationRunner (com.android.server.wm)
doFrame:-1, SurfaceAnimationRunner$$ExternalSyntheticLambda1 (com.android.server.wm)
run:1127, Choreographer$CallbackRecord (android.view)
doCallbacks:918, Choreographer (android.view)
doFrame:828, Choreographer (android.view)
run:1114, Choreographer$FrameDisplayEventReceiver (android.view)
handleCallback:938, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:210, Looper (android.os)
loop:299, Looper (android.os)
run:67, HandlerThread (android.os)
run:46, ServiceThread (com.android.server)
我们来看下SurfaceAnimationRunner#startAnimations、startPendingAnimationsLocked方法
private void startAnimations(long frameTimeNanos) {
synchronized (mLock) {
startPendingAnimationsLocked();
}
mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
}
private void startPendingAnimationsLocked() {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
//从mPendingAnimations取出待执行的动画
startAnimationLocked(mPendingAnimations.valueAt(i));
}
mPendingAnimations.clear();
}
2.11 SurfaceAnimationRunner#startAnimationLocked
这里就是最终动画执行的地方了
private void startAnimationLocked(RunningAnimation a) {
//通过AnimatorFactory构建一个动画驱动器
final ValueAnimator anim = mAnimatorFactory.makeAnimator();
// Animation length is already expected to be scaled.
anim.overrideDurationScale(1.0f);
anim.setDuration(a.mAnimSpec.getDuration());
//设置updateListener来处理每一帧动画
anim.addUpdateListener(animation -> {
synchronized (mCancelLock) {
if (!a.mCancelled) {
final long duration = anim.getDuration();
long currentPlayTime = anim.getCurrentPlayTime();
if (currentPlayTime > duration) {
currentPlayTime = duration;
}
applyTransformation(a, mFrameTransaction, currentPlayTime);
}
}
// 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.
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;
//添加该ValueAnimator到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);
}
//手动控制动画框架 立即开始应用动画,否则动画开始时间就是下一帧渲染的时间(就会有延迟的问题)
anim.doAnimationFrame(mChoreographer.getFrameTime());
}
AppTransition动画的触发时序图如下:
总结
Activity动画过程总结就到这了,本文针对对transition动画流程(准备阶段、触发过程)进行了整理总结,对于一些细节性的可能还不太够,后期会继续整理总结窗口动画等相关流程以及Choreographer、绘制这些原理性的知识。如有错误,欢迎指正!! :)