Android 12 S WindowManager Transition动画介绍
如果活动在同一任务中启动新活动,则现有活动的活动记录属于打开的应用程序,同时,新活动的另一个活动记录属于关闭的应用程序。//有些情况下,我们打开/关闭一个新的task/activity,但在现实中,只有一个半透明的活动在现有activities的顶部打开/关闭。//在以下情况下允许应用状态更新和动画:activity 正在转换可见性状态,或者activity 在我们有机会播放过渡动画之前被标记为隐
目录
1 AppTransitionController#handleAppTransitionReady
1.1 AppTransitionController#getTransitCompatType
1.1.1 AppTransitionController#getAnimationTargets
1.1.1.1 AppTransitionController#shouldApplyAnimation
1.2 AppTransitionController#applyAnimations
1.2.1 AppTransitionController#applyAnimations
1.2.2 WindowContainer#applyAnimation
1.2.3 WindowContainer#applyAnimationUnchecked
1.2.4 WindowContainer#getAnimationAdapter
1.2.5 WindowContainer#startAnimation
1.2.6 SurfaceAnimator#startAnimation
1.2.7 LocalAnimationAdapter#startAnimation
1.2.8 WindowContainer#startAnimation
1.2.9 SurfaceAnimationRunner#startAnimations
1.2.10 SurfaceAnimationRunner#startPendingAnimationsLocked
1.2.11 SurfaceAnimationRunner#startAnimationLocked
WindowManager old动画类型
WMS中全部动画如下
frameworks/base/core/java/android/view/WindowManager.java
WindowManagerWrapper | AppTransition | Value | 含义 |
---|---|---|---|
TRANSIT_UNSET | TRANSIT_OLD_UNSET | -1 | 没有设置动画 |
TRANSIT_NONE | TRANSIT_OLD_NONE | 0 | 没有动画 |
TRANSIT_ACTIVITY_OPEN | TRANSIT_OLD_ACTIVITY_OPEN | 6 | 新activity窗口将在同一Task中的现有窗口之上打开。 |
TRANSIT_ACTIVITY_CLOSE | TRANSIT_OLD_ACTIVITY_CLOSE | 7 | 最顶层activity窗口正在关闭,以显示同一任务中的前一个activity。 |
TRANSIT_TASK_OPEN | TRANSIT_OLD_TASK_OPEN | 8 | 新Task中的窗口将在另一个activity的Task中的现有窗口之上打开。 |
TRANSIT_TASK_CLOSE | TRANSIT_OLD_TASK_CLOSE | 9 | 最顶层activity窗口正在关闭,以显示另一个Task中的前一个activity |
TRANSIT_TASK_TO_FRONT | TRANSIT_OLD_TASK_TO_FRONT | 10 | 一个已存在的Task中的window正在显示到另一个activity的Task的最前端 |
TRANSIT_TASK_TO_BACK | TRANSIT_OLD_TASK_TO_BACK | 11 | 一个已存在的Task中的window插入到所有Tasks的最下面 |
TRANSIT_WALLPAPER_CLOSE | TRANSIT_OLD_WALLPAPER_CLOSE | 12 | 一个不存在Wallpaper的新window窗口正在打开在一个存在Wallpaper的activity窗口之上,故需要关闭Wallpaper |
TRANSIT_WALLPAPER_OPEN | TRANSIT_OLD_WALLPAPER_OPEN | 13 | 一个存在Wallpaper的新activity窗口正在打开在一个不存在Wallpaper的activity窗口之上,故需要开启Wallpaper |
TRANSIT_WALLPAPER_INTRA_OPEN | TRANSIT_OLD_WALLPAPER _INTRA_OPEN | 14 | 一个新activity窗口打开在另一个已经存在的activity窗口之上,而且两者都在壁纸之上 |
TRANSIT_WALLPAPER_INTRA_CLOSE | TRANSIT_OLD_WALLPAPER _INTRA_CLOSE | 15 | 一个最顶层的activity窗口正在关闭,以显示前一个activity,而且两者都在壁纸之上 |
TRANSIT_TASK_OPEN_BEHIND | TRANSIT_OLD_TASK _OPEN_BEHIND | 16 | 一个新Task正在打开在另一个现有activity的Task之上后,新窗口将短暂显示,然后消失 |
TRANSIT_ACTIVITY_RELAUNCH | TRANSIT_OLD_ACTIVITY _RELAUNCH | 18 | 一个activity正在被重新加载 |
TRANSIT_KEYGUARD_GOING_AWAY | TRANSIT_OLD_KEYGUARD _GOING_AWAY | 20 | 锁屏正在消失 |
TRANSIT_KEYGUARD_GOING _AWAY_ON_WALLPAPER | TRANSIT_OLD_KEYGUARD _GOING _AWAY_ON_WALLPAPER | 21 | Keyguard is going away with showing an activity behind that requests wallpaper. |
TRANSIT_KEYGUARD_OCCLUDE | TRANSIT_OLD_KEYGUARD _OCCLUDE | 22 | 锁屏正在被覆盖 |
TRANSIT_KEYGUARD_UNOCCLUDE | TRANSIT_OLD_KEYGUARD _UNOCCLUDE | 23 | 锁屏正在解除覆盖 |
TRANSIT_OLD_TRANSLUCENT _ACTIVITY_OPEN | 24 | 一个透明的activity正在打开 | |
TRANSIT_OLD_TRANSLUCENT _ACTIVITY_CLOSE | 25 | 一个透明的activity正在关闭 | |
TRANSIT_OLD_CRASHING _ACTIVITY_CLOSE | 26 | 一个crash的activity正在关闭 | |
TRANSIT_OLD_TASK_CHANGE _WINDOWING_MODE | 27 | 一个Task正在改变windowing mode |
WindowManager动画类型
DisplayContent动画 | Vlaue | 含义 |
---|---|---|
TRANSIT_NONE | 0 | 没有设置动画 |
TRANSIT_OPEN | 1 | 创建了一个以前不存在的窗口并使其可见。 |
TRANSIT_CLOSE | 2 | 一个可见的窗口不再存在(已完成或已销毁)。 |
TRANSIT_TO_FRONT | 3 | 已经存在但不可见的窗口变得可见。 |
TRANSIT_TO_BACK | 4 | 一个可见的窗口变成了不可见,但仍然存在。 |
TRANSIT_RELAUNCH | 5 | @hide |
TRANSIT_CHANGE | 6 | 窗口在前后都是可见的,但会以某种方式发生变化(例如:它调整大小或改变窗口模式)。 |
TRANSIT_KEYGUARD_GOING_AWAY | 7 | keyguard可见,并被dismissed |
TRANSIT_KEYGUARD_OCCLUDE | 8 | 一个窗口出现在一个locked的锁屏之上 |
TRANSIT_KEYGUARD_UNOCCLUDE | 9 | A window is made invisible revealing a locked keyguard. |
TRANSIT_FIRST_CUSTOM | 10 | 第一个slot用于自定义转换类型。调用者(如Shell)可以使用自定义转换类型来处理特殊情况。这些类型被Core有效地忽略,只是作为TransitionInfo对象的一部分传递。一个例子是使用自定义类型的snap-to-解散操作进行分屏。通过使用自定义类型,Shell可以正确地将转换的结果分派给分屏实现。 |
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void prepareAppTransition(@WindowManager.TransitionType int transit) { prepareAppTransition(transit, 0 /* flags */); } void prepareAppTransition(@WindowManager.TransitionType int transit, @WindowManager.TransitionFlags int flags) { final boolean prepared = mAppTransition.prepareAppTransition(transit, flags); if (prepared && okToAnimate()) { mSkipAppTransitionAnimation = false; } }
frameworks/base/services/core/java/com/android/server/wm/AppTransition.java
boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) { if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) { return false; } //后文用到的mNextAppTransitionRequests就是在此处添加进去的 mNextAppTransitionRequests.add(transit); mNextAppTransitionFlags |= flags; updateBooster(); removeAppTransitionTimeoutCallbacks(); mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable, APP_TRANSITION_TIMEOUT_MS); return prepare(); }
1 AppTransitionController#handleAppTransitionReady
/**处理要显示的APP过渡动画
* Handle application transition for given display.
*/
void handleAppTransitionReady() {
mTempTransitionReasons.clear();
//判断动画是否准备好
if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)|| !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)) {
return;//未准备好,则直接return
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
// TODO(new-app-transition): Remove code using appTransition.getAppTransition()
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) {
// Clearing the mAnimatingExit flag before entering animation. It's set to true if app window is removed, or window relayout to invisible. This also affects window visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the transition selection depends on wallpaper target visibility.
//在进入动画之前清除 mAnimatingExit flag。
如果移除应用窗口或将窗口重新布局设置为不可见,则设置该flag为 true。
这个flag也会影响窗口可见性。
我们需要在maybeUpdateTransitToWallpaper之前清除这个flag,因为过渡选择取决于壁纸target可见性。
mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
}
appCount = mDisplayContent.mChangingContainers.size();
for (int i = 0; i < appCount; ++i) {
// Clearing for same reason as above.
//清除flag----和上面一样的原因
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.
//在我们拉取lower/upper target之前调整壁纸,因为pending changes(如上面的 clearAnimatingFlags())可能会影响壁纸目标结果。或者,打开的应用窗口应该是wallpaper target
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
mDisplayContent.mOpeningApps);
//将动画类型转化为old动画类型,后面会具体介绍这个方法,见1.1
final @TransitionOldType int transit = getTransitCompatType(
mDisplayContent.mAppTransition,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
mDisplayContent.mSkipAppTransitionAnimation);
mDisplayContent.mSkipAppTransitionAnimation = false;
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.
//在tokens中找到最顶层应用程序窗口的布局参数,这将控制动画主题。如果所有关闭的窗口都被遮挡,则无需制作动画。例如,当这种过渡是在dream窗口(屏保)后面完成时,就是这种情况。
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes);
final ActivityRecord topOpeningApp =
getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
final ActivityRecord topClosingApp =
getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
final ActivityRecord topChangingApp =
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps) || containsVoiceInteraction(mDisplayContent.mOpeningApps);
final int layoutRedo;
//推迟开始动画
mService.mSurfaceAnimationRunner.deferStartingAnimations();
try {
//对WindowContainer apply animations,会执行startAnimation
然后对mOpeningApps.ActivityRecord和mClosingApps.ActivityRecord中的mOverrideTaskTransition赋值为false,见1.2
applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, animLp, voiceInteraction);
handleClosingApps();见1.3
handleOpeningApps();见1.4
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 {
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
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();
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;
}
1.1 AppTransitionController#getTransitCompatType
在getTransitCompatType中会对动画做转换,将动画转化为old类型的动画
/**根据当前的过渡动画请求获取旧的过渡动画类型
* Get old transit type based on the current transit requests.
*//管理app过渡动画状态
* @param appTransition {@link AppTransition} for managing app transition state.
//正在变成可见的activity
* @param openingApps {@link ActivityRecord}s which are becoming visible.
//正在变成不可见的activity
* @param closingApps {@link ActivityRecord}s which are becoming invisible.
//wallpaperTarget 如果不是null, 说明这是与壁纸相关联的当前可见窗口。
* @param wallpaperTarget If non-null, this is the currently visible window that is associated with the wallpaper.
//当前可见的窗口,是与壁纸相关联的,以防我们正在从一个与壁纸有关联的activity过渡到一个与壁纸没有关联的activity。否则无效。
* @param oldWallpaper The currently visible window that is associated with the wallpaper in case we are transitioning from an activity with a wallpaper to one without. Otherwise null.*/
static @TransitionOldType int getTransitCompatType(
AppTransition appTransition,
ArraySet<ActivityRecord> openingApps,
ArraySet<ActivityRecord> closingApps,
@Nullable WindowState wallpaperTarget,
@Nullable WindowState oldWallpaper,
boolean skipAppTransitionAnimation) {
// Determine if closing and opening app token sets are wallpaper targets, in which case special animations are needed.//判断当前closing或者opening的Apptoken的目标是否是壁纸,在这种情况下需要特殊的动画。
final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps)
&& wallpaperTarget != null;
final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps)
&& wallpaperTarget != null;
// Keyguard transit has highest priority.//keyguard过渡动画具有最高优先权。
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.//当这是一个正在closing的app, 并且锁屏早已经被一个activity覆盖,另一个activity被启动到顶端,所以所有普通APP过渡动画应该被使用
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.
//这不是一个锁屏过渡动画,并且其中一个APP请求跳过App过渡动画,最后一个入参决定
if (skipAppTransitionAnimation) {
return WindowManager.TRANSIT_OLD_UNSET;
}
//获取appTransition过渡动画Flag
final @TransitionFlags int flags = appTransition.getTransitFlags();
//获取第一个不是TRANSIT_NONE类型,也不是TRANSIT_KEYGUARD_GOING_AWAY,TRANSIT_KEYGUARD_OCCLUDE,TRANSIT_KEYGUARD_UNOCCLUDE三个中的一种的动画
final @TransitionType int firstTransit = appTransition.getFirstAppTransition();
// Special transitions//特别的过渡动画
// TODO(new-app-transitions): Revisit if those can be rewritten by using flags.
//判断appTransition是否包含TRANSIT_CHANGE
if (appTransition.containsTransitRequest(TRANSIT_CHANGE)) {
return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
}
//判断appTransition过渡动画Flag是否包含TRANSIT_FLAG_APP_CRASHED标志
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. */
//有些情况下,我们打开/关闭一个新的task/activity,但在现实中,只有一个半透明的活动在现有activities的顶部打开/关闭。对于这个,我们有一个不同的动画,因为没有一个task/activity动画与半透明app协调的好。
if (isNormalTransit(firstTransit)) {//如果firstTransit是TRANSIT_OPEN,TRANSIT_CLOSE,TRANSIT_TO_FRONT,TRANSIT_TO_BACK中的一种动画
boolean allOpeningVisible = true;
boolean allTranslucentOpeningApps = !openingApps.isEmpty();
//先取Arrayset中的最后一位,依次往前遍历
for (int i = openingApps.size() - 1; i >= 0; i--) {
//取最后一个activity
final ActivityRecord activity = openingApps.valueAt(i);
//如果openingApps中有一个activity是不可见的,则allOpeningVisible为false,并且在此条件之上,如果这个activity是充满Parent的,则allTranslucentOpeningApps为false
if (!activity.isVisible()) {
allOpeningVisible = false;
if (activity.fillsParent()) {
allTranslucentOpeningApps = false;
}
}
}
boolean allTranslucentClosingApps = !closingApps.isEmpty();
//如果closingApps中有一个activity充满Parent的,则allTranslucentClosingApps 为false,并中断for循环
for (int i = closingApps.size() - 1; i >= 0; i--) {
if (closingApps.valueAt(i).fillsParent()) {
allTranslucentClosingApps = false;
break;
}
}
//如果closingApps中没有充满parent的,并且openingApps都可见,返回一个透明的activity正在关闭的动画
if (allTranslucentClosingApps && allOpeningVisible) {
return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
}
//如果当前openingApps没有一个是充满Parent的并且closingApps非空,则返回一个透明的activity被打开的动画
if (allTranslucentOpeningApps && closingApps.isEmpty()) {
return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN;
}
}
final ActivityRecord topOpeningApp = getTopApp(openingApps,
false /* ignoreHidden */);
final ActivityRecord topClosingApp = getTopApp(closingApps,
true /* ignoreHidden */);
//如果当前closingApp和openingApp tokens都有Wallpaper,在这种情况下需要特殊的动画。
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) {
//我们正在从有墙纸的activity 过渡到没有墙纸的activity 。
// 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
//我们正在从没有墙纸的activity 过渡到有墙纸的activity
return TRANSIT_OLD_WALLPAPER_OPEN;
}
//返回一组要进行动画的WindowContainers
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;
//appTransition.mNextAppTransitionRequests如果包含TRANSIT_TO_FRONT并且isTaskOpening为true
if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && isTaskOpening) {
return TRANSIT_OLD_TASK_TO_FRONT;
}
//appTransition.mNextAppTransitionRequests如果包含TRANSIT_TO_BACK并且isTaskClosing为true
if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && isTaskClosing) {
return TRANSIT_OLD_TASK_TO_BACK;
}
//appTransition.mNextAppTransitionRequests如果包含TRANSIT_OPEN
if (appTransition.containsTransitRequest(TRANSIT_OPEN)) {
if (isTaskOpening) {
return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0 ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN;
}
if (isActivityOpening) {
return TRANSIT_OLD_ACTIVITY_OPEN;
}
}
//appTransition.mNextAppTransitionRequests如果包含TRANSIT_CLOSE
if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) {
if (isTaskClosing) {
return TRANSIT_OLD_TASK_CLOSE;
}
if (isActivityClosing) {
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;
}
下面介绍一下getAnimationTargets函数:
1.1.1 AppTransitionController#getAnimationTargets
/**查找一组要进行动画opening和closing的apps的WindowContainers。如果可能,我们将动画目标提升到窗口层次结构中的更高级别。
Find WindowContainers to be animated from a set of opening and closing apps. We will promote animation targets to higher level in the window hierarchy if possible.
* @param visible {@code true} to get animation targets for opening apps, {@code false} to get animation targets for closing apps.//visible=true,为获取opening apps的动画目标;visible=false,为 closing apps获取动画目标
* @return {@link WindowContainer}s to be animated.
//返回一组要进行动画的WindowContainers.
*/
@VisibleForTesting
static ArraySet<WindowContainer> getAnimationTargets(
ArraySet<ActivityRecord> openingApps,
ArraySet<ActivityRecord> closingApps,
boolean visible) {
// The candidates of animation targets, which might be able to promote to higher level.
//动画目标的候选者,也许能够晋升到更高的水平。
final LinkedList<WindowContainer> candidates = new LinkedList<>();
final ArraySet<ActivityRecord> apps = visible ? openingApps : closingApps;
for (int i = 0; i < apps.size(); ++i) {
final ActivityRecord app = apps.valueAt(i);
if (app.shouldApplyAnimation(visible)) {
candidates.add(app);//openingApps.valueAt(i)添加到candidates
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Changing app %s visible=%b performLayout=%b",app, app.isVisible(), false);
}
}
final ArraySet<ActivityRecord> otherApps = visible ? closingApps : openingApps;
// Ancestors of closing apps while finding animation targets for opening apps, or ancestors of opening apps while finding animation targets for closing apps.
//在查找用于打开应用的动画目标时关闭应用的祖先,或在查找用于关闭应用程序的动画目标时打开应用程序的祖先。
final ArraySet<WindowContainer> otherAncestors = new ArraySet<>();
for (int i = 0; i < otherApps.size(); ++i) {
for (WindowContainer wc = otherApps.valueAt(i); wc != null; wc = wc.getParent()) {
otherAncestors.add(wc);//closingApps .valueAt(i)添加到otherAncestors
}
}
// The final animation targets which cannot promote to higher level anymore.
//最终的动画目标,无法再提升到更高的水平。
final ArraySet<WindowContainer> targets = new ArraySet<>();
final ArrayList<WindowContainer> siblings = new ArrayList<>();
while (!candidates.isEmpty()) {
final WindowContainer current = candidates.removeFirst();
final WindowContainer parent = current.getParent();
siblings.clear();
siblings.add(current);
boolean canPromote = true;
if (parent == null || !parent.canCreateRemoteAnimationTarget()
// We cannot promote the animation on Task's parent when the task is in clearing task in case the animating get stuck when performing the opening task that behind it.
//当任务处于清除任务状态时,我们无法在任务的父级上推动动画,以防动画在执行其后面的打开任务时卡住。
|| (current.asTask() != null && current.asTask().mInRemoveTask)) {
canPromote = false;
} else {
// In case a descendant of the parent belongs to the other group, we cannot promote the animation target from "current" to the parent.
// Example: Imagine we're checking if we can animate a Task instead of a set of ActivityRecords. In case an activity starts a new activity within a same Task, an ActivityRecord of an existing activity belongs to the opening apps, at the same time, the other ActivityRecord of a new activity belongs to the closing apps. In this case, we cannot promote the animation target to Task level, but need to animate each individual activity.
//如果parent的后代属于另一个组,则无法将动画目标从“当前”提升到parent。
示例:假设我们正在检查是否可以对任务而不是一组活动记录进行动画处理。如果活动在同一任务中启动新活动,则现有活动的活动记录属于打开的应用程序,同时,新活动的另一个活动记录属于关闭的应用程序。在这种情况下,我们无法将动画目标提升到任务级别,但需要对每个单独的活动进行动画处理。
// [Task] +- [ActivityRecord1] (in opening apps)
// +- [ActivityRecord2] (in closing apps)
if (otherAncestors.contains(parent)) {
canPromote = false;
}
// Find all siblings of the current WindowContainer in "candidates", move them into a separate list "siblings", and checks if an animation target can be promoted to its parent. We can promote an animation target to its parent if and only if all visible siblings will be animating.
// Example: Imagine that a Task contains two visible activity record, but only one of them is included in the opening apps and the other belongs to neither opening or closing apps. This happens when an activity launches another translucent activity in the same Task. In this case, we cannot animate Task, but have to animate each activity, otherwise an activity behind the translucent activity also animates.
//在“候选项”中查找当前 WindowContainer 的所有同级,将它们移动到单独的“同级”列表中,并检查动画目标是否可以提升为其父级。我们可以将动画目标提升到其父级,当且仅当所有可见的同级都将进行动画处理。
示例:假设一个任务包含两个可见的活动记录,但其中只有一个包含在打开的应用程序中,另一个不属于打开或关闭的应用程序。当一个活动在同一任务中启动另一个半透明活动时,就会发生这种情况。在这种情况下,我们不能对 Task 进行动画处理,而必须对每个活动进行动画处理,否则半透明活动后面的活动也会进行动画处理。
// [Task] +- [ActivityRecord1] (visible, in opening apps)
// +- [ActivityRecord2] (visible, not in opening apps)
for (int j = 0; j < parent.getChildCount(); ++j) {
final WindowContainer sibling = parent.getChildAt(j);
if (candidates.remove(sibling)) {
siblings.add(sibling);
} else if (sibling != current && sibling.isVisible()) {
canPromote = false;
}
}
}
if (canPromote) {
candidates.add(parent);
} else {
targets.addAll(siblings);
}
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "getAnimationTarget in=%s, out=%s", apps, targets);
return targets;
}
1.1.1.1 AppTransitionController#shouldApplyAnimation
/**检查是否应在应用过渡过程中更新此ActivityRecord的可见性
如果这个ActivityRecord的可见性早已被设置为mVisible, 我们就不需要更新可见性,所以返回false
* Check if visibility of this {@link ActivityRecord} should be updated as part of an app transition. If the visibility of this {@link ActivityRecord} is already set to {@link #mVisible}, we don't need to update the visibility. So {@code false} is returned.</p>
* @param visible {@code true} if this {@link ActivityRecord} should become visible, {@code false} if this should become invisible.
//visible=true,这个ActivityRecord应该变为可见;
//visible=false,这个ActivityRecord应该变为不可见;
* @return {@code true} if visibility of this {@link ActivityRecord} should be updated, and an app transition animation should be run.
*/
boolean shouldApplyAnimation(boolean visible) {
// Allow for state update and animation to be applied if: activity is transitioning visibility state or the activity was marked as hidden and is exiting before we had a chance to play the transition animation or this is an opening app and windows are being replaced (e.g. freeform window to normal window).
//在以下情况下允许应用状态更新和动画:activity 正在转换可见性状态,或者activity 在我们有机会播放过渡动画之前被标记为隐藏并且正在退出,或者这是一个打开的应用程序并且窗口正在被替换(例如,自由格式窗口到普通窗口)。
return isVisible() != visible || mRequestForceTransition || (!isVisible() && mIsExiting)
|| (visible && forAllWindows(WindowState::waitingForReplacement, true));
}
1.2 AppTransitionController#applyAnimations
/**基于一组 ActivityRecord 应用app过渡动画
* Apply an app transition animation based on a set of {@link ActivityRecord}
*
* @param openingApps The list of opening apps to which an app transition animation applies.一组应用了app过渡动画的opening apps的列表。
* @param closingApps The list of closing apps to which an app transition animation applies.一组应用了app过渡动画的closing apps的列表。
* @param transit The current transition type.当前的动画类型
* @param animLp Layout parameters in which an app transition animation runs.
执行app过渡动画的layout参数
* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice interaction session driving task.如果此过渡中的某个应用属于语音交互会话驾驶任务。
*/
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;//如果入参transit没有设置动画,或者openingApps和closingApps为空,则直接return.
}
//获取openingApps对应的WindowContainer
final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
openingApps, closingApps, true /* visible */);
//获取closingApps对应的WindowContainer
final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
openingApps, closingApps, false /* visible */);
//执行动画
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);
}
}
1.2.1 AppTransitionController#applyAnimations
/**将动画应用于 window containers。
* Apply animation to the set of window containers.*/
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++) {//依次获取WindowContainer
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.
//如果将app过渡动画目标提升到更高级别,则 SurfaceAnimator 仅在提升的target上触发 WC#onAnimationFinish。因此,我们需要注意在每个 ActivityRecord 上触发 AR#onAnimationFinish,这是应用程序转换的一部分。
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.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
}
}
1.2.2 WindowContainer#applyAnimation
frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
/**根据窗口层次结构中的给定布局属性应用app过渡动画。
* Applies the app transition animation according the given the layout properties in the window hierarchy.
* @param sources {@link ActivityRecord}s which causes this app transition animation.要去应用此app过渡动画的ActivityRecord。
* @return {@code true} when the container applied the app transition, {@code false} if the app transition is disabled or skipped. 当container应用app过渡动画时返回 true,如果禁用或跳过APP过渡动画,则返回 false。
*
* @see #getAnimationAdapter
*/
boolean applyAnimation(
WindowManager.LayoutParams lp, @TransitionOldType int transit,
boolean enter, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
if (mWmService.mDisableTransitionAnimation) {//获取config_disableTransitionAnimation的值,这个config是系统配置的,可以配置是否允许all apps执行动画
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: transition animation is disabled or skipped. "+ "container=%s", this);
cancelAnimation();如果禁用all apps动画,则取消动画并return
return false;
}
// Only apply an animation if the display isn't frozen. If it is frozen, there is no reason to animate and it can cause strange artifacts when we unfreeze the display if some different animation is running.//仅当屏幕未冻住frozen时应用动画。如果它被冻结,则没有理由进行动画处理,并且在我们解冻显示时,如果正在运行一些不同的动画,则可能会导致奇怪的伪影。
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();
}
1.2.3 WindowContainer#applyAnimationUnchecked
protected void applyAnimationUnchecked(
WindowManager.LayoutParams lp, boolean enter,
@TransitionOldType int transit, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
final Task task = asTask();
//如果task不为空并且是不可见,并且task不是home或者recent时,并且当task是 IME 目标并执行任务关闭过渡到下一个任务时,附加并显示 IME 屏幕截图。
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;
// Attach and show the IME screenshot when the task is the IME target and performing task closing transition to the next task.当task是 IME目标并执行task关闭过渡到下一个任务时,attach并显示 IME 屏幕截图。
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);
}
startAnimation(getPendingTransaction(), adapter, !isVisible(),//见1.2.5
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) -> { });
}
}
}
-------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------
1.2.4 WindowContainer#getAnimationAdapter
/**根据窗口层次结构中给定的窗口布局属性获取动画适配器。
* Gets the {@link AnimationAdapter} according the given window layout properties in the window hierarchy.
* @return The return value will always contain two elements, one for normal animations and the other for thumbnail animation, both can be {@code null}.
返回值将始终包含两个元素,一个用于普通动画,另一个用于缩略图动画,两者都可以是null
* @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord
* @See LocalAnimationAdapter
*/
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);
final RemoteAnimationController controller =
getDisplayContent().mAppTransition.getRemoteAnimationController();
final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
&& isChangingAppTransition();
// Delaying animation start isn't compatible with remote animations at all.
//与远程动画完全不兼容时延迟动画开始
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);
} else if (isChanging) {//isChanging为true
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 {//普通动画
mNeedsAnimationBoundsLayer = (appRootTaskClipMode == ROOT_TASK_CLIP_AFTER_ANIM);
final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
if (a != null) {
// Only apply corner radius to animation if we're not in multi window mode. We don't want rounded corners when in pip or split screen.
final float windowCornerRadius = !inMultiWindowMode()
? getDisplayContent().getWindowCornerRadius() : 0;
AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
getDisplayContent().mAppTransition.canSkipFirstFrame(),
appRootTaskClipMode, true /* isAppAnimation */, windowCornerRadius), getSurfaceAnimationRunner());
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;
}
public WindowChangeAnimationSpec(Rect startBounds, Rect endBounds, DisplayInfo displayInfo, float durationScale, boolean isAppAnimation, boolean isThumbnail) {
mStartBounds = new Rect(startBounds);
mEndBounds = new Rect(endBounds);
mIsAppAnimation = isAppAnimation;
mIsThumbnail = isThumbnail;
createBoundsInterpolator((int) (ANIMATION_DURATION * durationScale), displayInfo);
}
/**
* This animator behaves slightly differently depending on whether the window is growing or shrinking: If growing, it will do a clip-reveal after quicker fade-out/scale of the smaller (old) snapshot. If shrinking, it will do an opposite clip-reveal on the old snapshot followed by a quicker fade-out of the bigger (old) snapshot while simultaneously shrinking the new window into place.
此动画器的行为略有不同,具体取决于窗口是增大还是缩小:如果增大,它将在较小(旧)快照的更快淡出/缩放后执行剪辑显示。如果缩小,它将在旧快照上执行相反的剪辑显示,然后更快地淡出较大(旧)快照,同时将新窗口缩小到位。
* place.
* @param duration
* @param displayInfo
*/
private void createBoundsInterpolator(long duration, DisplayInfo displayInfo) {
boolean growing = mEndBounds.width() - mStartBounds.width()
+ mEndBounds.height() - mStartBounds.height() >= 0;
float scalePart = 0.7f;
long scalePeriod = (long) (duration * scalePart);
float startScaleX = scalePart * ((float) mStartBounds.width()) / mEndBounds.width()
+ (1.f - scalePart);
float startScaleY = scalePart * ((float) mStartBounds.height()) / mEndBounds.height()
+ (1.f - scalePart);
if (mIsThumbnail) {
AnimationSet animSet = new AnimationSet(true);
Animation anim = new AlphaAnimation(1.f, 0.f);
anim.setDuration(scalePeriod);
if (!growing) {
anim.setStartOffset(duration - scalePeriod);
}
animSet.addAnimation(anim);
float endScaleX = 1.f / startScaleX;
float endScaleY = 1.f / startScaleY;
anim = new ScaleAnimation(endScaleX, endScaleX, endScaleY, endScaleY);
anim.setDuration(duration);
animSet.addAnimation(anim);
mAnimation = animSet;
mAnimation.initialize(mStartBounds.width(), mStartBounds.height(),
mEndBounds.width(), mEndBounds.height());
} else {
AnimationSet animSet = new AnimationSet(true);
final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1);
scaleAnim.setDuration(scalePeriod);
if (!growing) {
scaleAnim.setStartOffset(duration - scalePeriod);
}
animSet.addAnimation(scaleAnim);
final Animation translateAnim = new TranslateAnimation(mStartBounds.left,
mEndBounds.left, mStartBounds.top, mEndBounds.top);
translateAnim.setDuration(duration);
animSet.addAnimation(translateAnim);
Rect startClip = new Rect(mStartBounds);
Rect endClip = new Rect(mEndBounds);
startClip.offsetTo(0, 0);
endClip.offsetTo(0, 0);
final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
clipAnim.setDuration(duration);
animSet.addAnimation(clipAnim);
mAnimation = animSet;
mAnimation.initialize(mStartBounds.width(), mStartBounds.height(),
displayInfo.appWidth, displayInfo.appHeight);
}
}
1.2.5 WindowContainer#startAnimation
frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
/**在容器上启动动画。
* Starts an animation on the container.
*
* @param anim The animation to run.
* @param hidden Whether our container is currently hidden. TODO This should use isVisible at some point but the meaning is too weird to work for all containers.
* @param type The type of animation defined as {@link AnimationType}.
* @param animationFinishedCallback The callback being triggered when the animation finishes.
*/
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.
//这应该使用 isVisible() ,但由于 isVisible 目前有一个非常奇怪的含义,这并不适用于所有可动画窗口容器。
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
mSurfaceFreezer);
}
1.2.6 SurfaceAnimator#startAnimation
private AnimationAdapter mAnimation;
/**
* Starts an animation.
*
* @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the component responsible for running the animation. It runs the animation with {@link AnimationAdapter#startAnimation} once the hierarchy with the Leash has been set up.
* @param hidden Whether the container holding the child surfaces is currently visible or not. This is important as it will start with the leash hidden or visible before handing it to the component that is responsible to run the animation.
* @param animationFinishedCallback The callback being triggered when the animation finishes.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable SurfaceFreezer freezer) {
//先cancel动画
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;
}
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
}
frameworks/base/core/java/android/view/SurfaceControl.java static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface, Transaction t, @AnimationType int type, int width, int height, int x, int y, boolean hidden, Supplier<Transaction> transactionFactory) { if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash"); final SurfaceControl.Builder builder = animatable.makeAnimationLeash() .setParent(animatable.getAnimationLeashParent()) .setName(surface + " - animation-leash of " + animationTypeToString(type)) // TODO(b/151665759) Defer reparent calls // We want the leash to be visible immediately because the transaction which shows the leash may be deferred but the reparent will not. This will cause the leashed surface to be invisible until the deferred transaction is applied. If this doesn't work, you will can see the 2/3 button nav bar flicker during seamless rotation. 我们希望leash立即可见,因为显示leash的事务可能会被延迟,但reparent不会。这将导致leash图层在应用延迟事务之前是不可见的。如果这不起作用,你会看到2/3按钮导航条在无缝旋转时闪烁。 .setHidden(hidden) .setEffectLayer() .setCallsite("SurfaceAnimator.createAnimationLeash"); final SurfaceControl leash = builder.build(); t.setWindowCrop(leash, width, height); t.setPosition(leash, x, y); t.show(leash); t.setAlpha(leash, hidden ? 0 : 1); t.reparent(surface, leash); return leash; }frameworks/base/core/java/android/view/SurfaceControl.java /** * Re-parents a given layer to a new parent. Children inherit transform (position, scaling) crop, visibility, and Z-ordering from their parents, as if the children were pixels within the parent Surface. *将给定图层的父级重新转换为新的父层。子项从其父项继承transform(位置、缩放)裁剪、可见性和 Z 排序,就好像子项是父图层的像素一样。 * @param sc The SurfaceControl to reparent * @param newParent The new parent for the given control. * @return This Transaction */ @NonNull public Transaction reparent(@NonNull SurfaceControl sc, @Nullable SurfaceControl newParent) { checkPreconditions(sc); long otherObject = 0; if (newParent != null) { newParent.checkNotReleased(); otherObject = newParent.mNativeObject; } nativeReparent(mNativeObject, sc.mNativeObject, otherObject); mReparentedSurfaces.put(sc, newParent); return this; }frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
@Override public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) { if (mAnimatingActivityRegistry != null) { mAnimatingActivityRegistry.notifyStarting(this); } // If the animation needs to be cropped then an animation bounds layer is created as a child of the root pinned task or animation layer. The leash is then reparented to this new layer. 如果需要裁剪动画,则会创建一个动画边界图层作为根固定任务或动画图层的子级。然后将leash重新设置为此新层的父级。 if (mNeedsAnimationBoundsLayer) { mTmpRect.setEmpty(); if (getDisplayContent().mAppTransitionController.isTransitWithinTask( getTransit(), task)) { task.getBounds(mTmpRect); } else { final Task rootTask = getRootTask(); if (rootTask == null) { return; } // Set clip rect to root task bounds.将裁剪rect设置为根任务边界。 rootTask.getBounds(mTmpRect); } mAnimationBoundsLayer = createAnimationBoundsLayer(t); // Crop to root task bounds.裁剪为根root task t.setLayer(leash, 0); t.setLayer(mAnimationBoundsLayer, getAnimationLayer()); // Reparent leash to animation bounds layer. 将leash设置为动画边界图层的父级。 t.reparent(leash, mAnimationBoundsLayer); } }
1.2.7 LocalAnimationAdapter#startAnimation
frameworks/base/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
class LocalAnimationAdapter implements AnimationAdapter {@Override public void startAnimation(SurfaceControl animationLeash, Transaction t, @AnimationType int type, OnAnimationFinishedCallback finishCallback) { mAnimator.startAnimation(mSpec, animationLeash, t, () -> finishCallback.onAnimationFinished(type, this)); }
1.2.8 WindowContainer#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);
if (!mAnimationStartDeferred) {
mChoreographer.postFrameCallback(this::startAnimations);
}
// Some animations (e.g. move animations) require the initial transform to be applied immediately.
//某些动画(例如移动动画)需要立即应用初始transform 。
applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
}
}
1.2.9 SurfaceAnimationRunner#startAnimations
private void startAnimations(long frameTimeNanos) { synchronized (mLock) { startPendingAnimationsLocked(); } mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0); }
1.2.10 SurfaceAnimationRunner#startPendingAnimationsLocked
@GuardedBy("mLock") private void startPendingAnimationsLocked() { for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { startAnimationLocked(mPendingAnimations.valueAt(i)); } mPendingAnimations.clear(); }
1.2.11 SurfaceAnimationRunner#startAnimationLocked
@GuardedBy("mLock")
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;
}
applyTransformation(a, mFrameTransaction, currentPlayTime);
}
}
// Transaction will be applied in the commit phase.
//过渡动画将在commit阶段被应用
scheduleApplyTransaction();
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
synchronized (mCancelLock) {
if (!a.mCancelled) {
// TODO: change this back to use show instead of alpha when b/138459974 is fixed.
mFrameTransaction.setAlpha(a.mLeash, 1);
}
}
}
@Override
public void onAnimationEnd(Animator animation) {
synchronized (mLock) {
mRunningAnimations.remove(a.mLeash);
synchronized (mCancelLock) {
if (!a.mCancelled) {
// Post on other thread that we can push final state without jank.
在不卡顿的情况下,在其他线程上发布我们可以推送最终状态。
mAnimationThreadHandler.post(a.mFinishCallback);
}
}
}
}
});
a.mAnim = anim;
mRunningAnimations.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());
}
更多推荐
所有评论(0)