前言
接著上篇文章分析
Android T 遠(yuǎn)程動(dòng)畫(huà)顯示流程其一
切入點(diǎn)——處理應(yīng)用的顯示過(guò)渡
下面,我們以從桌面點(diǎn)擊一個(gè)應(yīng)用啟動(dòng)的場(chǎng)景來(lái)分析遠(yuǎn)程動(dòng)畫(huà)的流程,窗口添加的流程見(jiàn)Android T WMS窗口相關(guān)流程
這里我們從AppTransitionController.handleAppTransitionReady方法開(kāi)始跟蹤代碼流程
代碼路徑:framework/services/core/java/com/android/server/wm/AppTransitionController.java
/**
* Handle application transition for given display.
*/
void handleAppTransitionReady() {
......
//通過(guò)getTransitCompatType方法獲取transit的值
@TransitionOldType final int transit = getTransitCompatType(
mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers,
mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
mDisplayContent.mSkipAppTransitionAnimation);
......
//方法收集正在打開(kāi) (mOpeningApps)、關(guān)閉 (mClosingApps) 和切換 (mChangingContainers) 的應(yīng)用的activity類型
//并將它們存儲(chǔ)在 activityTypes 集合中。
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
//被用于查找與給定transit和activityTypes相關(guān)的 ActivityRecord
//也就是我們當(dāng)前打開(kāi)的應(yīng)用的ActivityRecord
final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes,
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
mDisplayContent.mChangingContainers);
//獲取正在打開(kāi)的應(yīng)用列表 (mOpeningApps) 中的頂層應(yīng)用。
//ignoreHidden 參數(shù)設(shè)置為 false,意味著即使應(yīng)用是隱藏的,也會(huì)被考慮在內(nèi)
final ActivityRecord topOpeningApp =
getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
//獲取正在關(guān)閉的應(yīng)用列表 (mClosingApps) 中的頂層應(yīng)用
final ActivityRecord topClosingApp =
getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
//獲取正在切換的應(yīng)用列表 (mChangingContainers) 中的頂層應(yīng)用
//其取決于參數(shù)DisplayContent.mChangingContainers中是否有值
/**
有三種情況會(huì)給DisplayContent.mChangingContainers中添加值
1.{@link Task}在全屏和浮窗之間發(fā)生切換
2.{@link TaskFragment}已組織好并且正在更改窗口邊界
3.{@link ActivityRecord}被重新分配到一個(gè)有組織的{@link TaskFragment}中
**/
final ActivityRecord topChangingApp =
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
//從之前找到的animLpActivity(正在打開(kāi)的應(yīng)用的ActivityRecord)的窗口中獲取布局參數(shù)
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
......
try {
/*1.1應(yīng)用app transition動(dòng)畫(huà)(遠(yuǎn)程動(dòng)畫(huà))*/
applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
animLp, voiceInteraction);
/*1.2.1處理closing activity可見(jiàn)性*/
handleClosingApps();
/*1.2.2處理opening actvity可見(jiàn)性*/
handleOpeningApps();
//處理用于處理正在切換的應(yīng)用
handleChangingApps(transit);
//處理正在關(guān)閉或更改的容器
handleClosingChangingContainers();
//設(shè)置與最后一次應(yīng)用過(guò)渡動(dòng)畫(huà)相關(guān)的信息
appTransition.setLastAppTransition(transit, topOpeningApp,
topClosingApp, topChangingApp);
final int flags = appTransition.getTransitFlags();
/*1.3播放遠(yuǎn)程動(dòng)畫(huà)*/
layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
//處理非應(yīng)用窗口的過(guò)渡動(dòng)畫(huà)
handleNonAppWindowsInTransition(transit, flags);
//執(zhí)行動(dòng)畫(huà)回調(diào)
appTransition.postAnimationCallback()
} finally {
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
......
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
/*2.由于activity的可見(jiàn)性變更,將DisplayContent.mLayoutNeeded標(biāo)志位置為true*/
mDisplayContent.setLayoutNeeded();
......
}
這個(gè)方法主要處理這三件事:
1.處理activity的過(guò)渡動(dòng)畫(huà)(遠(yuǎn)程動(dòng)畫(huà))
2.分別調(diào)用 handleClosingApps以及handleOpeningApps對(duì)要關(guān)閉的和要打開(kāi)的Activity進(jìn)行可見(jiàn)性更新。
3.調(diào)用AppTransition.goodToGo方法走播放遠(yuǎn)程動(dòng)畫(huà)流程。
4.由于activity的可見(jiàn)性變更,將DisplayContent.mLayoutNeeded設(shè)置為true,該標(biāo)志位在DisplayContent.performLayoutNoTrace中用來(lái)判斷是否對(duì)當(dāng)前DisplayContent下的所有窗口進(jìn)行刷新。
這里我們主要關(guān)注遠(yuǎn)程動(dòng)畫(huà)的流程,主要分為兩個(gè)部分。
- 處理并創(chuàng)建遠(yuǎn)程動(dòng)畫(huà)流程
applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, animLp, voiceInteraction);
- 播放顯示遠(yuǎn)程動(dòng)畫(huà)流程
layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
動(dòng)畫(huà)創(chuàng)建流程
applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, animLp, voiceInteraction);
基于一組ActivityRecord來(lái)應(yīng)用動(dòng)畫(huà),這些ActivityRecord表示正在進(jìn)行切換的應(yīng)用。mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps
這兩個(gè)參數(shù)分別代表正在打開(kāi)和關(guān)閉的應(yīng)用;transit
通過(guò)前面getTransitCompatType方法中獲取,是TRANSIT_OLD_WALLPAPER_CLOSE
(12);animLp
通過(guò)前面getAnimLp方法中獲取,用于定義窗口的布局參數(shù)。這里就是代表正在打開(kāi)的應(yīng)用的ActivityRecord的窗口布局參數(shù);voiceInteraction
:表示是否為語(yǔ)音交互。
處理并創(chuàng)建遠(yuǎn)程動(dòng)畫(huà)
代碼路徑:framework/services/core/java/com/android/server/wm/AppTransitionController.java
/**
* 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.
* @param closingApps The list of closing apps to which an app transition animation applies.
* @param transit The current transition type.
* @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<ActivityRecord> openingApps,
ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit,
LayoutParams animLp, boolean voiceInteraction) {
//方法檢查過(guò)渡類型是否未設(shè)置,或者打開(kāi)和關(guān)閉的應(yīng)用程序是否都為空。如果是,則方法直接返回,不執(zhí)行任何動(dòng)畫(huà)。
if (transit == WindowManager.TRANSIT_OLD_UNSET
|| (openingApps.isEmpty() && closingApps.isEmpty())) {
return;
}
//調(diào)用getAnimationTargets方法獲取打開(kāi)和關(guān)閉的應(yīng)用的窗口容器(WindowContainer)
final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
openingApps, closingApps, true /* visible */);
final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
openingApps, closingApps, false /* visible */);
//打開(kāi)和關(guān)閉的窗口應(yīng)用動(dòng)畫(huà)。這是通過(guò)調(diào)重載的applyAnimations方法完成的,傳遞相應(yīng)的參數(shù),如動(dòng)畫(huà)的目標(biāo)、過(guò)渡類型等。
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
//如果存在最近任務(wù)動(dòng)畫(huà)控制器(RecentsAnimationController),則發(fā)送任務(wù)出現(xiàn)任務(wù)
final RecentsAnimationController rac = mService.getRecentsAnimationController();
if (rac != null) {
rac.sendTasksAppeared();
}
//遍歷打開(kāi)和關(guān)閉的應(yīng)用,并設(shè)置mOverrideTaskTransition為false
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;
}
//如果存在輔助功能控制器(AccessibilityController)且有回調(diào),則調(diào)用其onAppWindowTransition方法。
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
if (accessibilityController.hasCallbacks()) {
accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
}
}
傳遞關(guān)鍵參數(shù),處理應(yīng)用程序窗口的打開(kāi)和關(guān)閉動(dòng)畫(huà)。
通過(guò)getAnimationTargets
方法獲取當(dāng)前打開(kāi)和關(guān)閉的應(yīng)用的容器,即ActivityRecord的容器。
最關(guān)鍵的方法是調(diào)用的applyAnimations方法:
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
我們這里openingWcs
和closingWcs
實(shí)際上表示的是應(yīng)用的容器,即Task;openingApps
和 closingApps
就是前面?zhèn)鬟f的mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps
,分別代表正在打開(kāi)和關(guān)閉的應(yīng)用,也是掛在對(duì)應(yīng)Task下面的ActivityRecord。并且傳遞了應(yīng)用的可見(jiàn)性visible
,true可見(jiàn),false不可見(jiàn)。
因此在我們桌面點(diǎn)擊打開(kāi)應(yīng)用的流程中,openingWcs
實(shí)際上指的是應(yīng)用的Task,openingApps
是應(yīng)用的ActivityRecord(其實(shí)就是應(yīng)用的主界面),其可見(jiàn)性為true;closingWcs
對(duì)應(yīng)的是桌面的Task,closingApps
是桌面的ActivityRecord,其可見(jiàn)性為false。
這也對(duì)應(yīng)了我們前面創(chuàng)建動(dòng)畫(huà)圖層的堆棧中所打印的,先創(chuàng)建了應(yīng)用的動(dòng)畫(huà)圖層,后創(chuàng)建桌面的動(dòng)畫(huà)圖層。
注:
從這里開(kāi)始后續(xù)流程執(zhí)行了兩次,第一次是打開(kāi)的應(yīng)用流程,第二次是關(guān)閉的應(yīng)用流程(一個(gè)應(yīng)用的啟動(dòng),伴隨這另一個(gè)應(yīng)用的退出,浮窗等特殊場(chǎng)景除外)。
從桌面點(diǎn)擊開(kāi)啟應(yīng)用的場(chǎng)景來(lái)說(shuō),一次是啟動(dòng)的應(yīng)用角度執(zhí)行流程,另一次是桌面角度執(zhí)行流程。
從代碼邏輯上來(lái)說(shuō),唯一的不同點(diǎn)是傳遞的可見(jiàn)性的值不同。
這個(gè)方法調(diào)用的是重載的applyAnimations方法
獲取需要做動(dòng)畫(huà)的容器
/**
* 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) {
//獲取窗口容器的數(shù)量
final int wcsCount = wcs.size();
//遍歷每一個(gè)應(yīng)用的窗口容器
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.
//對(duì)于每一個(gè)應(yīng)用的窗口容器,檢查正在進(jìn)行切換的應(yīng)用(apps)中哪些是該窗口容器的后代。
//就比如應(yīng)用的ActivityRecord是是應(yīng)用的Task的后代
final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
for (int j = 0; j < apps.size(); ++j) {
final ActivityRecord app = apps.valueAt(j);
//app如果是wc的后代,將其添加到一個(gè)列表中。
if (app.isDescendantOf(wc)) {
transitioningDescendants.add(app);
}
}
//調(diào)用每個(gè)應(yīng)用的窗口容器的applyAnimation方法,傳入相應(yīng)的參數(shù)
//這些參數(shù)包含動(dòng)畫(huà)的布局、過(guò)渡類型、是否可見(jiàn)、是否有語(yǔ)音交互以及需要做動(dòng)畫(huà)的ActivityRecord應(yīng)用的列表。
wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
}
}
入?yún)⒑x:wcs
: 一個(gè)WindowContainer對(duì)象的集合,這些對(duì)象是需要應(yīng)用動(dòng)畫(huà)的窗口容器。apps
: 一個(gè)ActivityRecord對(duì)象的集合,這些對(duì)象表示正在進(jìn)行切換的應(yīng)用程序。transit
: 當(dāng)前的過(guò)渡類型,例如淡入淡出、滑動(dòng)等。visible
: 一個(gè)布爾值,表示應(yīng)用是否變?yōu)榭梢?jiàn)。animLp
: 布局參數(shù),定義了動(dòng)畫(huà)運(yùn)行時(shí)的布局。voiceInteraction
: 一個(gè)布爾值,表示是否有語(yǔ)音交互。
關(guān)鍵代碼解讀:
-
final WindowContainer wc = wcs.valueAt(i);
獲取窗口容器wcs
是前面?zhèn)鬟f過(guò)來(lái)的是Task,wc
就是依次獲取當(dāng)前應(yīng)用的Task和桌面Task。 -
transitioningDescendants
存儲(chǔ)的就是需要做動(dòng)畫(huà)的ActivityRecord。 -
傳遞動(dòng)畫(huà)參數(shù)
通過(guò)wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
方法,傳遞參數(shù)動(dòng)畫(huà)的布局、過(guò)渡類型、是否可見(jiàn)、是否有語(yǔ)音交互以及需要做動(dòng)畫(huà)的ActivityRecord應(yīng)用的列表。wc
就是Task,其沒(méi)有applyAnimation方法,向上找父類WindowContainer.applyAnimation方法調(diào)用。
判斷是否應(yīng)用動(dòng)畫(huà),傳遞相關(guān)參數(shù)
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
/**
* Applies the app transition animation according the given the layout properties in the
* window hierarchy.
*
* @param lp The layout parameters of the window.
* @param transit The app transition type indicates what kind of transition to be applied.
* @param enter Whether the app transition is entering transition or not.
* @param isVoiceInteraction Whether the container is participating in voice interaction or not.
* @param sources {@link ActivityRecord}s which causes this app transition animation.
*
* @return {@code true} when the container applied the app transition, {@code false} if the
* app transition is disabled or skipped.
*
* @see #getAnimationAdapter
*/
boolean applyAnimation(WindowManager.LayoutParams lp, @TransitionOldType int transit,
boolean enter, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
//判斷是否禁用過(guò)渡動(dòng)畫(huà)
if (mWmService.mDisableTransitionAnimation) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: transition animation is disabled or skipped. "
+ "container=%s", this);
//取消當(dāng)前動(dòng)畫(huà)
cancelAnimation();
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.
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
//會(huì)判斷是否有凍結(jié),屏幕是否開(kāi)啟
if (okToAnimate()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: transit=%s, enter=%b, wc=%s",
AppTransition.appTransitionOldToString(transit), enter, this);
//傳遞相關(guān)參數(shù),創(chuàng)建AnimationAdapter和AnimationRunnerBuilder,準(zhǔn)備啟動(dòng)動(dòng)畫(huà)
applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
} else {
//取消當(dāng)前動(dòng)畫(huà)
cancelAnimation();
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
//檢查指定的窗口容器是否正在進(jìn)行動(dòng)畫(huà)
return isAnimating();
}
下面說(shuō)說(shuō)里面的幾個(gè)關(guān)鍵點(diǎn):
-
判斷是否禁用過(guò)渡動(dòng)畫(huà)
mWmService.mDisableTransitionAnimation
這個(gè)變量是在WindowManagerService的構(gòu)造方法中初始化的mDisableTransitionAnimation = context.getResources().getBoolean( com.android.internal.R.bool.config_disableTransitionAnimation);
可以發(fā)現(xiàn)是讀取
config_disableTransitionAnimation
配置項(xiàng)
代碼路徑:frameworks/base/core/res/res/values/symbols.xml<java-symbol type="bool" name="config_disableTransitionAnimation" />
定義了這個(gè)symbol
代碼路徑:frameworks/base/core/res/res/values/config.xml<!-- Flag to disable all transition animations --> <bool name="config_disableTransitionAnimation">false</bool>
定義了默認(rèn)值為false,不禁用過(guò)渡動(dòng)畫(huà)
-
取消當(dāng)前動(dòng)畫(huà)
cancelAnimation();
void cancelAnimation() { //處理動(dòng)畫(huà)結(jié)束時(shí)的一些后續(xù)操作 doAnimationFinished(mSurfaceAnimator.getAnimationType(), mSurfaceAnimator.getAnimation()); //調(diào)用SurfaceAnimator.cancelAnimation方法來(lái)取消當(dāng)前正在進(jìn)行的動(dòng)畫(huà) mSurfaceAnimator.cancelAnimation(); //調(diào)用unfreeze方法解除對(duì)顯示的凍結(jié)狀態(tài),允許顯示繼續(xù)正常更新和渲染 mSurfaceFreezer.unfreeze(getSyncTransaction()); }
doAnimationFinished方法在動(dòng)畫(huà)播放結(jié)束時(shí)處理回調(diào)邏輯中會(huì)調(diào)用到。
-
準(zhǔn)備動(dòng)畫(huà)
applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
把前面?zhèn)鬟f的參數(shù)動(dòng)畫(huà)的布局、過(guò)渡類型、是否可見(jiàn)、是否有語(yǔ)音交互以及需要做動(dòng)畫(huà)的ActivityRecord應(yīng)用的列表,再次傳遞到applyAnimationUnchecked方法中。
注意,這里調(diào)用的是Task中重寫的applyAnimationUnchecked方法,而不是直接調(diào)用的WindowContainer中的applyAnimationUnchecked方法。
因?yàn)槲覀兦懊媸峭ㄟ^(guò)前面AppTransitionController.applyAnimations中wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
調(diào)用過(guò)來(lái)的,因此此時(shí)的this指針指的是變量wc
,即應(yīng)用對(duì)應(yīng)的Task。
后面細(xì)講applyAnimationUnchecked方法。 -
檢查動(dòng)畫(huà)
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.javafinal boolean isAnimating(int flags, int typesToCheck) { return getAnimatingContainer(flags, typesToCheck) != null; }
flags
用于確定要檢查的動(dòng)畫(huà)類型和范圍。typesToCheck
用于確定哪些類型的動(dòng)畫(huà)需要檢查。
方法內(nèi)部調(diào)用getAnimatingContainer
方法來(lái)獲取正在進(jìn)行動(dòng)畫(huà)的窗口容器,并根據(jù)返回值判斷是否存在符合條件和目標(biāo)標(biāo)志的動(dòng)畫(huà)。
如果返回值為 true,則說(shuō)明存在符合條件的動(dòng)畫(huà);如果返回值為 false,則說(shuō)明不存在符合條件的動(dòng)畫(huà)。
處理最近任務(wù)狀態(tài)的動(dòng)畫(huà)
applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
其中參數(shù)enter
代表的其實(shí)就應(yīng)用的可見(jiàn)性,從前面AppTransitionController.applyAnimations方法中逐步傳遞過(guò)來(lái)值有兩個(gè)
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp, voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp, voiceInteraction);
啟動(dòng)的應(yīng)用的可見(jiàn)性為true,桌面的可見(jiàn)性為false
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/Task.java
@Override
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
@TransitionOldType int transit, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
//獲取RecentsAnimationController
//只有在最近任務(wù)中,切換到另一個(gè)應(yīng)用時(shí)才會(huì)創(chuàng)建
final RecentsAnimationController control = mWmService.getRecentsAnimationController();
//RecentsAnimationController不為空
if (control != null) {
// We let the transition to be controlled by RecentsAnimation, and callback task's
// RemoteAnimationTarget for remote runner to animate.
//應(yīng)用可見(jiàn)性為true,且當(dāng)前activity不是桌面或者最近任務(wù)
if (enter && !isActivityTypeHomeOrRecents()) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"applyAnimationUnchecked, control: %s, task: %s, transit: %s",
control, asTask(), AppTransition.appTransitionOldToString(transit));
//執(zhí)行最近任務(wù)動(dòng)畫(huà)邏輯
control.addTaskToTargets(this, (type, anim) -> {
for (int i = 0; i < sources.size(); ++i) {
sources.get(i).onAnimationFinished(type, anim);
}
});
}
//判斷是否有返回手勢(shì)
} else if (mBackGestureStarted) {
// Cancel playing transitions if a back navigation animation is in progress.
// This bit is set by {@link BackNavigationController} when a back gesture is started.
// It is used as a one-off transition overwrite that is cleared when the back gesture
// is committed and triggers a transition, or when the gesture is cancelled.
//返回手勢(shì)mBackGestureStarted標(biāo)志位置為false
mBackGestureStarted = false;
//設(shè)置一個(gè)標(biāo)志為true,表示應(yīng)跳過(guò)應(yīng)用的過(guò)渡動(dòng)畫(huà)。
mDisplayContent.mSkipAppTransitionAnimation = true;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Skipping app transition animation. task=%s", this);
} else {
//調(diào)用父類WindowContainer的applyAnimationUnchecked方法
super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
}
}
只有在最近任務(wù)中,切換到另一個(gè)應(yīng)用時(shí)才會(huì)創(chuàng)建RecentsAnimationController,因此control
的值為空。如果不為空,應(yīng)用可見(jiàn)性為true,且當(dāng)前activity不是桌面或者最近任務(wù),則會(huì)進(jìn)入到最近任務(wù)的動(dòng)畫(huà)處理邏輯。
我們?cè)诓僮鬟^(guò)程中也沒(méi)有返回手勢(shì),因此mBackGestureStarted
為false。
所以調(diào)用了父類WindowContainer的applyAnimationUnchecked方法。
獲取AnimationAdapter并創(chuàng)建動(dòng)畫(huà)圖層
接著前面super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
進(jìn)行分析
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
@TransitionOldType int transit, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
//獲取當(dāng)前Task
final Task task = asTask();
//當(dāng)前Task不為空,應(yīng)用變?yōu)椴豢梢?jiàn)狀態(tài),且應(yīng)用Task不為桌面或者最近任務(wù)
//可以理解為應(yīng)用按home鍵回到桌面的場(chǎng)景
if (task != null && !enter && !task.isActivityTypeHomeOrRecents()) {
//對(duì)輸入法相關(guān)insets做處理
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.
if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {
mDisplayContent.showImeScreenshot();
}
}
//創(chuàng)建AnimationAdapter
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
transit, enter, isVoiceInteraction);
//adapters.first指的是創(chuàng)建startBounds為空情況RemoteAnimationAdapterWrapper的對(duì)象
AnimationAdapter adapter = adapters.first;
//adapters.second指的是創(chuàng)建startBounds不為空情況RemoteAnimationAdapterWrapper的對(duì)象
AnimationAdapter thumbnailAdapter = adapters.second;
if (adapter != null) {
if (sources != null) {
//把需要做的動(dòng)畫(huà)的ActivityRecord添加到mSurfaceAnimationSources
mSurfaceAnimationSources.addAll(sources);
}
//創(chuàng)建AnimationRunnerBuilder
AnimationRunnerBuilder animationRunnerBuilder = new AnimationRunnerBuilder();
//isTaskTransitOld方法中根據(jù)transit的值判斷返回值,
//從桌面啟動(dòng)應(yīng)用時(shí)transit為12,表示TRANSIT_OLD_WALLPAPER_CLOSE
if (isTaskTransitOld(transit)) {
//設(shè)置過(guò)渡動(dòng)畫(huà)背景色
animationRunnerBuilder.setTaskBackgroundColor(getTaskAnimationBackgroundColor());
// TODO: Remove when we migrate to shell (b/202383002)
//mTaskTransitionSpec不為null
if (mWmService.mTaskTransitionSpec != null) {
//隱藏Insets溢出的部分
animationRunnerBuilder.hideInsetSourceViewOverflows(
mWmService.mTaskTransitionSpec.animationBoundInsets);
}
}
// Check if the animation requests to show background color for Activity and embedded
// TaskFragment.
//獲取當(dāng)前Activity,但我們當(dāng)前只有Task,因此為空
final ActivityRecord activityRecord = asActivityRecord();
//TaskFragment為Task父類,獲取到了當(dāng)前Task
final TaskFragment taskFragment = asTaskFragment();
//設(shè)置過(guò)渡動(dòng)動(dòng)畫(huà)背景色邏輯
if (adapter.getShowBackground()
// Check if it is Activity transition.
&& ((activityRecord != null && isActivityTransitOld(transit))
// Check if it is embedded TaskFragment transition.
|| (taskFragment != null && taskFragment.isEmbedded()
&& isTaskFragmentTransitOld(transit)))) {
final @ColorInt int backgroundColorForTransition;
if (adapter.getBackgroundColor() != 0) {
// If available use the background color provided through getBackgroundColor
// which if set originates from a call to overridePendingAppTransition.
backgroundColorForTransition = adapter.getBackgroundColor();
} else {
......
}
animationRunnerBuilder.setTaskBackgroundColor(backgroundColorForTransition);
}
//進(jìn)入創(chuàng)建動(dòng)畫(huà)圖層邏輯
animationRunnerBuilder.build()
.startAnimation(getPendingTransaction(), adapter, !isVisible(),
ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);
//判斷是否在動(dòng)畫(huà)中顯示壁紙
if (adapter.getShowWallpaper()) {
//更新pendingLayoutChanges添加標(biāo)志FINISH_LAYOUT_REDO_WALLPAPER,表示需要重新處理壁紙布局。
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
}
這個(gè)方法主要就是獲取AnimationAdapter,創(chuàng)建對(duì)應(yīng)的動(dòng)畫(huà)圖層,我們下面主要討論這兩個(gè)點(diǎn)。
創(chuàng)建RemoteAnimationRecord
創(chuàng)建RemoteAnimationRecord,并在其中創(chuàng)建RemoteAnimationAdapterWrapper,在通過(guò)RemoteAnimationAdapterWrapper實(shí)現(xiàn)AnimationAdapter接口
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
transit, enter, isVoiceInteraction);
調(diào)用的是WindowContainer中的getAnimationAdapter方法,傳遞了參數(shù)動(dòng)畫(huà)的布局、過(guò)渡類型、是否可見(jiàn)、是否有語(yǔ)音交互。
/**
* 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}.
* @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;
//獲取當(dāng)前窗口的裁剪模式
final int appRootTaskClipMode = getDisplayContent().mAppTransition.getAppRootTaskClipMode();
// Separate position and size for use in animators.
//獲取屏幕大小,參數(shù)appRootTaskClipMode在這個(gè)方法中無(wú)實(shí)際意義
final Rect screenBounds = getAnimationBounds(appRootTaskClipMode);
//mTmpRect大小賦值為屏幕大小
mTmpRect.set(screenBounds);
//transit值為12,TRANSIT_OLD_WALLPAPER_CLOSE,不進(jìn)入該流程
if (this.asTask() != null && isTaskTransitOld(transit)) {
this.asTask().adjustAnimationBoundsForTransition(mTmpRect);
}
//設(shè)置動(dòng)畫(huà)位置為左上角頂點(diǎn)(0, 0)
getAnimationPosition(mTmpPoint);
mTmpRect.offsetTo(0, 0);
final AppTransition appTransition = getDisplayContent().mAppTransition;
//創(chuàng)建RemoteAnimationController
final RemoteAnimationController controller = appTransition.getRemoteAnimationController();
//AppTransition.isChangeTransitOld(transit),transit值為12,返回false
//enter 應(yīng)用可見(jiàn)性,啟動(dòng)應(yīng)用的可見(jiàn)性為true,桌面可見(jiàn)性為false
//isChangingAppTransition(),判斷當(dāng)前應(yīng)用是否切換過(guò)渡,
//其取決于參數(shù)DisplayContent.mChangingContainers中是否有值
/**
有三種情況會(huì)給DisplayContent.mChangingContainers中添加值
1.{@link Task}在全屏和浮窗之間發(fā)生切換
2.{@link TaskFragment}已組織好并且正在更改窗口邊界
3.{@link ActivityRecord}被重新分配到一個(gè)有組織的{@link TaskFragment}中
**/
//這里我們isChangingAppTransition()值為false,即isChanging值為false
final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
&& isChangingAppTransition();
// Delaying animation start isn't compatible with remote animations at all.
//mSurfaceAnimator.isAnimationStartDelayed()判斷動(dòng)畫(huà)是否延遲開(kāi)啟,
//false表示不延遲,true表示延遲,我們這里值為false
if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
// Here we load App XML in order to read com.android.R.styleable#Animation_showBackdrop.
boolean showBackdrop = false;
// Optionally set backdrop color if App explicitly provides it through
// {@link Activity#overridePendingTransition(int, int, int)}.
@ColorInt int backdropColor = 0;
//isFromActivityEmbedding(),返回的是RemoteAnimationController中的變量mIsActivityEmbedding
//我們這里前面通過(guò)getRemoteAnimationController()創(chuàng)建RemoteAnimationController會(huì)對(duì)其賦值為false
//因此下面流程不進(jìn)行
if (controller.isFromActivityEmbedding()) {
......
}
//創(chuàng)建一個(gè)和mTmpRect相同大小和位置的矩陣
final Rect localBounds = new Rect(mTmpRect);
localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
final RemoteAnimationController.RemoteAnimationRecord adapters;
//前面isChanging的值為false,因此!isChanging為true
//enter 應(yīng)用可見(jiàn)性,啟動(dòng)應(yīng)用的可見(jiàn)性為true,桌面可見(jiàn)性為false
//這里主要判斷的是isClosingWhenResizing()方法的值,
//這個(gè)值同樣取決于參數(shù)DisplayContent.mChangingContainers中是否有值
//這里我們isClosingWhenResizing()值為false
if (!isChanging && !enter && isClosingWhenResizing()) {
// Container that is closing while resizing. Pass in the closing start bounds, so
// the animation can start with the correct bounds, there won't be a snapshot.
// Cleanup the mClosingChangingContainers so that when the animation is finished, it
// will reset the surface.
//調(diào)整大小時(shí)正在關(guān)閉的容器。傳入關(guān)閉的開(kāi)始邊界,這樣動(dòng)畫(huà)就可以從正確的邊界開(kāi)始,不會(huì)有快照。
//清理mClosingChangingContainers,以便在動(dòng)畫(huà)完成時(shí)重置surface。
final Rect closingStartBounds = getDisplayContent().mClosingChangingContainers
.remove(this);
adapters = controller.createRemoteAnimationRecord(
this, mTmpPoint, localBounds, screenBounds, closingStartBounds,
showBackdrop, false /* shouldCreateSnapshot */);
} else {
//isChanging為false,所以startBounds為null
final Rect startBounds = isChanging ? mSurfaceFreezer.mFreezeBounds : null;
//創(chuàng)建RemoteAnimationRecord,之后創(chuàng)建RemoteAnimationAdapterWrapper
//RemoteAnimationAdapterWrapper實(shí)現(xiàn)了AnimationAdapter接口
adapters = controller.createRemoteAnimationRecord(
this, mTmpPoint, localBounds, screenBounds, startBounds, showBackdrop);
}
if (backdropColor != 0) {
adapters.setBackDropColor(backdropColor);
}
if (!isChanging) {
//根據(jù)enter的值設(shè)置RemoteAnimationRecord中的mMode的值
adapters.setMode(enter
? RemoteAnimationTarget.MODE_OPENING
: RemoteAnimationTarget.MODE_CLOSING);
}
//RemoteAnimationAdapterWrapper會(huì)根據(jù)startBounds變量是否為空進(jìn)行不同的創(chuàng)建方式
//startBounds變量為空的情況,保存到Pair類的first變量中
//startBounds變量不為空的情況,保存到Pair類的second變量中
resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
} else if (isChanging) {
//創(chuàng)建的是LocalAnimationAdapter,非遠(yuǎn)程動(dòng)畫(huà)流程
......
} else {
//創(chuàng)建的是LocalAnimationAdapter,非遠(yuǎn)程動(dòng)畫(huà)流程
......
}
return resultAdapters;
}
通過(guò)創(chuàng)建RemoteAnimationRecord來(lái)創(chuàng)建RemoteAnimationAdapterWrapper
adapters = controller.createRemoteAnimationRecord(
this, mTmpPoint, localBounds, screenBounds, startBounds, showBackdrop);
根據(jù)前面設(shè)置的參數(shù)我們可以知道傳遞的值分別為:this
指的是應(yīng)用Task,以及桌面Task;startBounds
為 null ;mTmpPoint
為(0,0) ;localBounds
為屏幕分辨率的矩形; showBackdrop
為false。
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/RemoteAnimationController.java
/**
* Creates an animation record for each individual {@link WindowContainer}.
*
* @param windowContainer The windows to animate.
* @param position The position app bounds relative to its parent.
* @param localBounds The bounds of the app relative to its parent.
* @param endBounds The end bounds after the transition, in screen coordinates.
* @param startBounds The start bounds before the transition, in screen coordinates.
* @param showBackdrop To show background behind a window during animation.
* @return The record representing animation(s) to run on the app.
*/
RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
Point position, Rect localBounds, Rect endBounds, Rect startBounds,
boolean showBackdrop) {
return createRemoteAnimationRecord(windowContainer, position, localBounds, endBounds,
startBounds, showBackdrop, startBounds != null /* shouldCreateSnapshot */);
}
/**
* Creates an animation record for each individual {@link WindowContainer}.
*
* @param windowContainer The windows to animate.
* @param position The position app bounds relative to its parent.
* @param localBounds The bounds of the app relative to its parent.
* @param endBounds The end bounds after the transition, in screen coordinates.
* @param startBounds The start bounds before the transition, in screen coordinates.
* @param showBackdrop To show background behind a window during animation.
* @param shouldCreateSnapshot Whether this target should create a snapshot animation.
* @return The record representing animation(s) to run on the app.
*/
RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
Point position, Rect localBounds, Rect endBounds, Rect startBounds,
boolean showBackdrop, boolean shouldCreateSnapshot) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
windowContainer);
final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
localBounds, endBounds, startBounds, showBackdrop, shouldCreateSnapshot);
//添加RemoteAnimationRecord對(duì)象到mPendingAnimations中
mPendingAnimations.add(adapters);
return adapters;
}
傳遞參數(shù),創(chuàng)建RemoteAnimationRecord,其中shouldCreateSnapshot
參數(shù)的值取決于startBounds
是否為空,我們這里startBounds
為空,所以shouldCreateSnapshot
為false。
需要注意的是,這里把創(chuàng)建的RemoteAnimationRecord對(duì)保存到了mPendingAnimations
列表中,先添加的是應(yīng)用的RemoteAnimationRecord,第二次添加的是桌面的RemoteAnimationRecord。
/**
* Contains information about a remote-animation for one WindowContainer. This keeps track of,
* potentially, multiple animating surfaces (AdapterWrappers) associated with one
* Window/Transition. For example, a change transition has an adapter controller for the
* main window and an adapter controlling the start-state snapshot.
* <p>
* This can be thought of as a bridge between the information that the remote animator sees (via
* {@link RemoteAnimationTarget}) and what the server sees (the
* {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces).
*/
public class RemoteAnimationRecord {
//RemoteAnimationAdapterWrapper用來(lái)實(shí)現(xiàn)AnimationAdapter接口
RemoteAnimationAdapterWrapper mAdapter;
RemoteAnimationAdapterWrapper mThumbnailAdapter = null;
//RemoteAnimationTarget主要用來(lái)存放動(dòng)畫(huà)圖層
RemoteAnimationTarget mTarget;
//mWindowContainer保存當(dāng)前容器
final WindowContainer mWindowContainer;
final Rect mStartBounds;
final boolean mShowBackdrop;
@ColorInt int mBackdropColor = 0;
private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING;
RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
Rect endBounds, @Nullable Rect startBounds, boolean showBackdrop,
boolean shouldCreateSnapshot) {
mWindowContainer = windowContainer;
mShowBackdrop = showBackdrop;
if (startBounds != null) {
......
} else {
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
new Rect(), mShowBackdrop);
mStartBounds = null;
}
}
......
}
在RemoteAnimationRecord構(gòu)造方法中創(chuàng)建RemoteAnimationAdapterWrapper。
RemoteAnimationAdapterWrapper會(huì)根據(jù)startBounds變量是否為空進(jìn)行不同的創(chuàng)建方式,我們這里主要關(guān)注其為空的流程。startBounds
為空,走else流程
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
new Rect(), mShowBackdrop);
這里通過(guò)new Rect()
空矩陣(上下左右四點(diǎn)均為0),創(chuàng)建了一個(gè)startBounds
class RemoteAnimationAdapterWrapper implements AnimationAdapter {
......
RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
Rect localBounds, Rect endBounds, Rect startBounds, boolean showBackdrop) {
mRecord = record;
mPosition.set(position.x, position.y);
mLocalBounds = localBounds;
mEndBounds.set(endBounds);
mStartBounds.set(startBounds);
mShowBackdrop = showBackdrop;
}
......
把傳遞的參數(shù)賦值給了RemoteAnimationAdapterWrapper的成員變量。
這里我們的RemoteAnimationAdapterWrapper實(shí)現(xiàn)了AnimationAdapter接口。
通過(guò)startAnimation方法創(chuàng)建動(dòng)畫(huà)圖層
animationRunnerBuilder.build()
.startAnimation(getPendingTransaction(), adapter, !isVisible(),
ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);
getPendingTransaction()
:指的是一個(gè)事務(wù)。adapter
:是前面賦值AnimationAdapter adapter = adapters.first;
,即創(chuàng)建的startBounds變量為空情況的RemoteAnimationAdapterWrapper對(duì)象。!isVisible()
:獲取當(dāng)前可見(jiàn)性后取反。應(yīng)用當(dāng)前可見(jiàn)性為false,即傳遞true;桌面當(dāng)前可見(jiàn)性為true,即傳遞false。ANIMATION_TYPE_APP_TRANSITION
:過(guò)渡動(dòng)畫(huà)類型。thumbnailAdapter
:是前面賦值AnimationAdapter thumbnailAdapter = adapters.second;
,即創(chuàng)建的startBounds變量不為空情況的RemoteAnimationAdapterWrapper對(duì)象。
animationRunnerBuilder
是AnimationRunnerBuilder對(duì)象,調(diào)用了其build方法中的startAnimation方法。我們這里調(diào)用的startAnimation方法是一個(gè)函數(shù)式接口。
private interface IAnimationStarter {
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
}
startAnimation方法的實(shí)現(xiàn)在AnimationRunnerBuilder中的build方法中return的 (Transaction t, AnimationAdapter adapter, boolean hidden, AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> {......}
。
private class AnimationRunnerBuilder {
/**
* Runs when the surface stops animating
*/
private final List<Runnable> mOnAnimationFinished = new LinkedList<>();
/**
* Runs when the animation is cancelled but the surface is still animating
*/
private final List<Runnable> mOnAnimationCancelled = new LinkedList<>();
......
private IAnimationStarter build() {
return (Transaction t, AnimationAdapter adapter, boolean hidden,
@AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> {
startAnimation(getPendingTransaction(), adapter, !isVisible(), type,
(animType, anim) -> mOnAnimationFinished.forEach(Runnable::run),
() -> mOnAnimationCancelled.forEach(Runnable::run), snapshotAnim);
};
}
}
除了傳遞之前的參數(shù)之外,在return的(Transaction t, AnimationAdapter adapter, boolean hidden, AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> {......}
中調(diào)用了startAnimation方法,傳遞了回調(diào)函數(shù)(animType, anim) -> mOnAnimationFinished.forEach(Runnable::run)
和() -> mOnAnimationCancelled.forEach(Runnable::run)
,會(huì)執(zhí)行mOnAnimationFinished和mOnAnimationCancelled這兩個(gè)變量中的Runnable。
繼續(xù)查看startAnimation方法
/**
* 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.
* @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
* cancel call to the underlying AnimationAdapter.
* @param snapshotAnim The animation to run for the snapshot. {@code null} if there is no
* snapshot.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim) {
ProtoLog.v(WM_DEBUG_ANIM, "Starting animation on %s: type=%d, anim=%s",
this, type, anim);
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}
-
入?yún)⒑x
t
:這是一個(gè)對(duì)象,用于描述一系列的窗口操作,例如移動(dòng)、調(diào)整大小、繪制等。這些操作在WMS中排隊(duì),并在適當(dāng)?shù)臅r(shí)機(jī)應(yīng)用到窗口上。anim
:這是對(duì)動(dòng)畫(huà)進(jìn)行封裝的類。它包含了一些關(guān)于如何開(kāi)始、更新和結(jié)束動(dòng)畫(huà)的信息。傳遞的也就是前面WindowContainer.getAnimationAdapter中創(chuàng)建startBounds變量為空情況的RemoteAnimationAdapterWrapper對(duì)象。hidden
:這個(gè)布爾值表示窗口是否隱藏。如果窗口是隱藏的,那么就不會(huì)顯示動(dòng)畫(huà)。前面?zhèn)鬟f的!isVisible()
值為false。type
:這個(gè)整數(shù)代表了動(dòng)畫(huà)的類型。這里我們傳遞的是ANIMATION_TYPE_APP_TRANSITION
,即圖層上顯示的app_transition。animationFinishedCallback
和animationCancelledCallback
:這兩個(gè)是回調(diào)函數(shù),分別在動(dòng)畫(huà)完成和動(dòng)畫(huà)取消時(shí)被調(diào)用。snapshotAnim
:這個(gè)參數(shù)是給定動(dòng)畫(huà)的快照。如果參數(shù)為null,那么就表示沒(méi)有快照。傳遞過(guò)來(lái)的是變量thumbnailAdapter
,即startBounds變量不為空情況的RemoteAnimationAdapterWrapper對(duì)象。 -
代碼含義
關(guān)鍵的代碼只有這一句mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback, animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
這行代碼調(diào)用了SurfaceAnimator的startAnimation方法來(lái)啟動(dòng)動(dòng)畫(huà)。SurfaceAnimator的作用主要是控制窗口動(dòng)畫(huà),它是窗口動(dòng)畫(huà)的中控,通過(guò)操控mLeash對(duì)象來(lái)實(shí)現(xiàn)窗口的大小、位置、透明度等動(dòng)畫(huà)屬性的改變。這個(gè)方法需要一系列參數(shù),包括上面解釋的所有參數(shù),還有一個(gè)SurfaceFreezer對(duì)象mSurfaceFreezer
,它可以在動(dòng)畫(huà)開(kāi)始時(shí)凍結(jié)窗口的更新,以防止在動(dòng)畫(huà)過(guò)程中窗口的內(nèi)容閃爍。
mSurfaceAnimator和mSurfaceFreezer是在WindowContainer的構(gòu)造方法中初始化的class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E> implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable, InsetsControlTarget { ...... WindowContainer(WindowManagerService wms) { mWmService = wms; mTransitionController = mWmService.mAtmService.getTransitionController(); mPendingTransaction = wms.mTransactionFactory.get(); mSyncTransaction = wms.mTransactionFactory.get(); mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms); mSurfaceFreezer = new SurfaceFreezer(this, wms); } ...... }
通過(guò)SurfaceAnimator中創(chuàng)建leash
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
/**
* 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.
* @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
* cancel call to the underlying AnimationAdapter.
* @param snapshotAnim The animation to run for the snapshot. {@code null} if there is no
* snapshot.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
@Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
//開(kāi)始新的動(dòng)畫(huà)之前,取消之前的動(dòng)畫(huà)
//參數(shù)含義:t(是一個(gè)事務(wù)對(duì)象),true(表示動(dòng)畫(huà)正在重新啟動(dòng)),和true(表示向前取消)
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
//初始化參數(shù),把WindowContainer.startAnimation中傳遞的參數(shù)賦值給對(duì)應(yīng)變量
mAnimation = anim;
mAnimationType = type;
mSurfaceAnimationFinishedCallback = animationFinishedCallback;
mAnimationCancelledCallback = animationCancelledCallback;
//獲取當(dāng)前窗口的SurfaceControl
final SurfaceControl surface = mAnimatable.getSurfaceControl();
//沒(méi)有surface,則取消當(dāng)前的動(dòng)畫(huà)
if (surface == null) {
Slog.w(TAG, "Unable to start animation, surface is null or no children.");
cancelAnimation();
return;
}
//調(diào)用SurfaceFreezer中takeLeashForAnimation()獲取mLeash,但是SurfaceFreezer中沒(méi)有被初始化,所以這里的mLeash還是為null
mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
if (mLeash == null) {
//創(chuàng)建mLeash
mLeash = createAnimationLeash(mAnimatable, surface, t, type,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
0 /* y */, hidden, mService.mTransactionFactory);
//創(chuàng)建動(dòng)畫(huà)“l(fā)eash”后執(zhí)行的一些操作,包括重置圖層、重新分配圖層以及重置Surface的位置
mAnimatable.onAnimationLeashCreated(t, mLeash);
}
//處理動(dòng)畫(huà)開(kāi)始時(shí)進(jìn)行一些設(shè)置和準(zhǔn)備工作
mAnimatable.onLeashAnimationStarting(t, mLeash);
if (mAnimationStartDelayed) {
ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable);
return;
}
//將leash傳給AnimationAdapter
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
mAnimation.dump(pw, "");
ProtoLog.d(WM_DEBUG_ANIM, "Animation start for %s, anim=%s", mAnimatable, sw);
}
//獲取一個(gè)快照,并使用該快照來(lái)執(zhí)行動(dòng)畫(huà),我們這里snapshotAnim為null,因此不涉及
if (snapshotAnim != null) {
mSnapshot = freezer.takeSnapshotForAnimation();
if (mSnapshot == null) {
Slog.e(TAG, "No snapshot target to start animation on for " + mAnimatable);
return;
}
mSnapshot.startAnimation(t, snapshotAnim, type);
}
}
入?yún)⒕褪乔懊鎃indowContainer.startAnimation中傳遞的參數(shù)。
獲取當(dāng)前窗口的surface
final SurfaceControl surface = mAnimatable.getSurfaceControl();
mAnimatable是Animatable接口的對(duì)象,WindowContainer實(shí)現(xiàn)了Animatable接口。
在WindowContainer構(gòu)造方法中初始化mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
而SurfaceAnimator的構(gòu)造方法是
SurfaceAnimator(Animatable animatable,
@Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback,
WindowManagerService service) {
mAnimatable = animatable;
mService = service;
mStaticAnimationFinishedCallback = staticAnimationFinishedCallback;
mInnerAnimationFinishedCallback = getFinishedCallback(staticAnimationFinishedCallback);
}
也就是說(shuō)實(shí)際上是把this
賦值給了mAnimatable
,因此mAnimatable
就代表了當(dāng)前的窗口。
從前面的代碼可以看出遠(yuǎn)程動(dòng)畫(huà)涉及到打開(kāi)的應(yīng)用和關(guān)閉的應(yīng)用,這兩個(gè)應(yīng)用的動(dòng)畫(huà)。
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
即我們這里第一次獲取當(dāng)前窗口mAnimatable
時(shí),代表的是正在打開(kāi)的應(yīng)用,即應(yīng)用Task;第二次獲取的則是桌面,即桌面Task。
獲取Leash
mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
根據(jù)freezer是否為null來(lái)確定mLeash的值,我們這里freezer
是從WindowContainer.startAnimation方法中傳遞過(guò)來(lái)的mSurfaceFreezer
,這個(gè)變量在WindowContainer的構(gòu)造方法中初始化mSurfaceFreezer = new SurfaceFreezer(this, wms);
,因此mSurfaceFreezer
不為null
,即freezer
不為null
,freezer != null
為true
,所以走freezer.takeLeashForAnimation()
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/SurfaceFreezer.java
SurfaceControl mLeash;
/**
* Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation.
* By transferring the leash, this will no longer try to clean-up the leash when finished.
*/
SurfaceControl takeLeashForAnimation() {
SurfaceControl out = mLeash;
mLeash = null;
return out;
}
mLeash在SurfaceFreezer類中并沒(méi)有初始化,因此我們的mLeash值為null,所以out的值同樣為null,最終SurfaceAnimator類中的mLeash獲取到的值為null
創(chuàng)建Leash
mLeash為null,使用createAnimationLeash方法創(chuàng)建Leash
mLeash = createAnimationLeash(mAnimatable, surface, t, type,
mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
0 /* y */, hidden, mService.mTransactionFactory);
入?yún)⒑xmAnimatable
:當(dāng)前窗口。surface
:當(dāng)前窗口的surface。t
:一個(gè)事務(wù)對(duì)象,用于執(zhí)行一系列操作。type
:動(dòng)畫(huà)類型。mAnimatable.getSurfaceWidth()
、mAnimatable.getSurfaceHeight()
:窗口surface尺寸的參數(shù)。0 /* x */
、 0 /* y */
:坐標(biāo)位置hidden
:一個(gè)布爾值,表示是否隱藏。mService.mTransactionFactory
:一個(gè)事務(wù)工廠對(duì)象,用于創(chuàng)建新的事務(wù)。
SurfaceAnimator.createAnimationLeash()
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java
static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
Transaction t, @AnimationType int type, int width, int height, int x, int y,
boolean hidden, Supplier<Transaction> transactionFactory) {
/* log add start*/
Slog.i("WindowManager:","createAnimationLeash type = " + animationTypeToString(type) , new Exception());
/* log add end*/
ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to leash for %s", animatable);
//通過(guò)SurfaceControl.Builder創(chuàng)建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.
.setHidden(hidden)
.setEffectLayer()
.setCallsite("SurfaceAnimator.createAnimationLeash");
//通過(guò)前面的SurfaceControl.Builder創(chuàng)建leash
final SurfaceControl leash = builder.build();
//其他屬性設(shè)置
t.setWindowCrop(leash, width, height);
t.setPosition(leash, x, y);
t.show(leash);
t.setAlpha(leash, hidden ? 0 : 1);
//當(dāng)前窗口的surface重新綁定到新創(chuàng)建的leash上
t.reparent(surface, leash);
return leash;
}
傳遞當(dāng)前窗口animatable
,為其和其父節(jié)點(diǎn)之間添加surface。第一次創(chuàng)建的是正在打開(kāi)的應(yīng)用的動(dòng)畫(huà),第二次是桌面關(guān)閉時(shí)的動(dòng)畫(huà)。
下面我們解讀一下,leash是如何創(chuàng)建并加入其中的
通過(guò)SurfaceControl.Builder創(chuàng)建leash
-
animatable.makeAnimationLeash()
代碼路徑: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.javapublic Builder makeAnimationLeash() { return makeSurface().setContainerLayer(); }
創(chuàng)建一個(gè)圖層作為容器layer
-
setParent(animatable.getAnimationLeashParent())
這段代碼我們分成兩個(gè)部分來(lái)看,即setParent()
和getAnimationLeashParent()
1.setParent()
代碼路徑:frameworks/base/core/java/android/view/SurfaceControl.java/** * Set a parent surface for our new SurfaceControl. * * Child surfaces are constrained to the onscreen region of their parent. * Furthermore they stack relatively in Z order, and inherit the transformation * of the parent. * * @param parent The parent control. */ @NonNull public Builder setParent(@Nullable SurfaceControl parent) { mParent = parent; return this; }
這個(gè)段代碼很簡(jiǎn)單,就是給當(dāng)前SurfaceControl設(shè)置一個(gè)父SurfaceControl。
2.getAnimationLeashParent()
代碼路徑: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java@Override public SurfaceControl getAnimationLeashParent() { return getParentSurfaceControl(); } /* * @return The SurfaceControl parent for this containers SurfaceControl. * The SurfaceControl must be valid if non-null. */ @Override public SurfaceControl getParentSurfaceControl() { final WindowContainer parent = getParent(); if (parent == null) { return null; } return parent.getSurfaceControl(); } /** * @return The SurfaceControl for this container. * The SurfaceControl must be valid if non-null. */ @Override public SurfaceControl getSurfaceControl() { return mSurfaceControl; }
簡(jiǎn)單來(lái)說(shuō),就是獲取當(dāng)前窗口父SurfaceControl。
那么合起來(lái)
setParent(animatable.getAnimationLeashParent())
的意思就是,把當(dāng)前新創(chuàng)建的SurfaceControl(leash)的父親設(shè)置為當(dāng)前窗口父親的SurfaceControl。
即此時(shí)leash圖層和當(dāng)前窗口應(yīng)用Task的父親均是DefaultTaskDsiplayArea,兩人還在當(dāng)兄弟。
簡(jiǎn)圖如下:
此時(shí)創(chuàng)建的是應(yīng)用Task的動(dòng)畫(huà),桌面Task的動(dòng)畫(huà)尚未創(chuàng)建,待應(yīng)用Task動(dòng)畫(huà)創(chuàng)建完成后,才會(huì)去走正在關(guān)閉的應(yīng)用(桌面)的動(dòng)畫(huà)邏輯。
桌面Task是一直掛在DefaultTaskDsiplayArea上的,這里我們先不關(guān)注桌面Task節(jié)點(diǎn)。 -
setEffectLayer()
代碼路徑:frameworks/base/core/java/android/view/SurfaceControl.java/** * Indicate whether an 'EffectLayer' is to be constructed. * * An effect layer behaves like a container layer by default but it can support * color fill, shadows and/or blur. These layers will not have an associated buffer. * When created, this layer has no effects set and will be transparent but the caller * can render an effect by calling: * - {@link Transaction#setColor(SurfaceControl, float[])} * - {@link Transaction#setBackgroundBlurRadius(SurfaceControl, int)} * - {@link Transaction#setShadowRadius(SurfaceControl, float)} * * @hide */ public Builder setEffectLayer() { mFlags |= NO_COLOR_FILL; //清空緩沖區(qū)設(shè)置 unsetBufferSize(); return setFlags(FX_SURFACE_EFFECT, FX_SURFACE_MASK); }
設(shè)置為EffectLayer。它是一種特殊類型的SurfaceControl層,它默認(rèn)表現(xiàn)得像一個(gè)容器層,但可以支持顏色填充、陰影和/或模糊效果。
這個(gè)EffectLayer主要就是用于實(shí)現(xiàn)一些視覺(jué)效果。
默認(rèn)的注釋里面也說(shuō)明可以使用這些方法來(lái)渲染一個(gè)效果:
Transaction#setColor(SurfaceControl, float[]):使用給定的顏色數(shù)組設(shè)置該層的顏色。
Transaction#setBackgroundBlurRadius(SurfaceControl, int):設(shè)置背景模糊的半徑。
Transaction#setShadowRadius(SurfaceControl, float):設(shè)置陰影的半徑。
final SurfaceControl leash = builder.build();
最后通過(guò)build()
方法創(chuàng)建leash
當(dāng)前窗口的surface重新綁定到新創(chuàng)建的leash上
t.reparent(surface, leash);
這里的surface
指的就是從前面?zhèn)鬟f的當(dāng)前窗口的SurfaceControl。
代碼路徑: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.
*
* @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) {
//檢查傳入的SurfaceControl對(duì)象是否滿足某些預(yù)設(shè)條件
checkPreconditions(sc);
long otherObject = 0;
if (newParent != null) {
//檢查新父對(duì)象是否被釋放。如果已經(jīng)被釋放,那么它會(huì)拋出異常。
newParent.checkNotReleased();
//新父對(duì)象不為null且未被釋放,那么將新父對(duì)象的Native對(duì)象賦值給otherObject。
otherObject = newParent.mNativeObject;
}
//傳入了三個(gè)參數(shù):1.當(dāng)前對(duì)象的Native對(duì)象 2.被重新設(shè)置父對(duì)象的SurfaceControl的Native對(duì)象 3.新父對(duì)象的Native對(duì)象。
//用于實(shí)現(xiàn)重新設(shè)置父對(duì)象的具體操作。
nativeReparent(mNativeObject, sc.mNativeObject, otherObject);
//把被重新設(shè)置父對(duì)象的SurfaceControl和新父對(duì)象存儲(chǔ)到mReparentedSurfaces這個(gè)map中。
mReparentedSurfaces.put(sc, newParent);
return this;
}
@NonNull SurfaceControl sc
: 表示要被重新設(shè)置父對(duì)象的SurfaceControl對(duì)象。這個(gè)參數(shù)不能為null。@Nullable SurfaceControl newParent
: 表示新的父SurfaceControl對(duì)象。可以為null,表示沒(méi)有新的父對(duì)象。
這個(gè)方法主要就是把當(dāng)前窗口的SurfaceControl的父親,修改為leash。mReparentedSurfaces
是ArrayMap對(duì)象,以鍵值對(duì)的形式臨時(shí)存儲(chǔ)父子關(guān)系,key值存儲(chǔ)SurfaceControl對(duì)象,value為其父SurfaceControl對(duì)象。
即此時(shí)leash圖層變成了當(dāng)前窗口應(yīng)用Task圖層的父親,如簡(jiǎn)易圖所示:
曾經(jīng)我們是兄弟,如今我是你爸爸~
需要注意的是,Task和DefaultTaskDsiplayArea容器之前的關(guān)系并未改變,創(chuàng)建leash圖層的過(guò)程只是改變的是surface之間的關(guān)系。
即leash圖層是DefaultTaskDsiplayArea圖層的父親,Task的圖層是leash圖層的父親,但是Task的父親仍然是DefaultTaskDsiplayArea。
實(shí)際關(guān)系情況如下圖所示:
所以DefaultTaskDsiplayArea和leash實(shí)際上是表面父子,leash和Task也是表面父子
leash的surface調(diào)整
mAnimatable.onAnimationLeashCreated(t, mLeash);
入?yún)⑹且粋€(gè)事務(wù)(用于操作窗口系統(tǒng)的底層API)和一個(gè)SurfaceControl對(duì)象(表示一個(gè)可以控制和操作Surface的接口)
把創(chuàng)建好的mLeash傳遞到onAnimationLeashCreated方法中,做一些Surface調(diào)整操作。
該方法實(shí)現(xiàn)在WindowContainer中。
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void reassignLayer(Transaction t) {
final WindowContainer parent = getParent();
if (parent != null) {
parent.assignChildLayers(t);
}
}
void resetSurfacePositionForAnimationLeash(Transaction t) {
t.setPosition(mSurfaceControl, 0, 0);
final SurfaceControl.Transaction syncTransaction = getSyncTransaction();
if (t != syncTransaction) {
// Avoid restoring to old position if the sync transaction is applied later.
syncTransaction.setPosition(mSurfaceControl, 0, 0);
}
mLastSurfacePosition.set(0, 0);
}
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
mLastLayer = -1;
mAnimationLeash = leash;
reassignLayer(t);
// Leash is now responsible for position, so set our position to 0.
resetSurfacePositionForAnimationLeash(t);
}
這段代碼主要用于在創(chuàng)建新的leash時(shí),重置動(dòng)畫(huà)目標(biāo)的位置,并初始化一些動(dòng)畫(huà)相關(guān)的狀態(tài)。同時(shí),可能還用于重新分配或者設(shè)置子容器的圖層。
首先,在新的動(dòng)畫(huà)(leash)被創(chuàng)建時(shí)被調(diào)用。在這個(gè)方法中,首先將mLastLayer
(可能表示上一個(gè)圖層或者上一個(gè)動(dòng)畫(huà)目標(biāo))設(shè)置為-1,然后保存?zhèn)魅氲?code>leash到mAnimationLeash
(后面removeLeash流程中會(huì)用到mAnimationLeash
)。
之后,調(diào)用reassignLayer(t)
方法,這個(gè)方法獲取這個(gè)視圖的父容器,如果父容器存在,那么就調(diào)用父容器的assignChildLayers(t)
方法(用于調(diào)整其所有child的z-order)。
最后,為了確保leash現(xiàn)在位置的控制,調(diào)用resetSurfacePositionForAnimationLeash(t)
方法將Surface的位置重置為(0,0),重置界面元素的位置以便進(jìn)行動(dòng)畫(huà)。
注:Z-order也被稱為深度順序(depth order)或Z軸順序,它用于確定圖層(Layers)在屏幕上的堆疊順序。簡(jiǎn)單來(lái)說(shuō),Z-order就是圖層在Z軸上的位置,Z軸位置越低,圖層越在底層,Z軸位置越高,圖層越在頂層。
處理動(dòng)畫(huà)開(kāi)始時(shí)進(jìn)行一些設(shè)置和準(zhǔn)備工作
mAnimatable.onLeashAnimationStarting(t, mLeash);
入?yún)⑼瑯邮且粋€(gè)事務(wù)(用于操作窗口系統(tǒng)的底層API)和一個(gè)SurfaceControl對(duì)象(表示一個(gè)可以控制和操作Surface的接口)
onLeashAnimationStarting方法是在ActivityRecord中實(shí)現(xiàn)的。
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
@Override
public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) {
if (mAnimatingActivityRegistry != null) {
//1.將正在啟動(dòng)或者有動(dòng)畫(huà)效果的Activity添加到列表中,以便于管理和控制這些Activity的動(dòng)畫(huà)效果。
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.
//2.否需要?jiǎng)?chuàng)建一個(gè)動(dòng)畫(huà)邊界層
if (mNeedsAnimationBoundsLayer) {
//設(shè)置臨時(shí)矩形為空
mTmpRect.setEmpty();
//調(diào)用方法檢查當(dāng)前的活動(dòng)轉(zhuǎn)移是否在任務(wù)內(nèi)部。
//如果是,則獲取任務(wù)的邊界到臨時(shí)矩形mTmpRect。如果不是,則獲取RootTask的邊界。
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.
rootTask.getBounds(mTmpRect);
}
//創(chuàng)建動(dòng)畫(huà)邊界層
mAnimationBoundsLayer = createAnimationBoundsLayer(t);
// Crop to root task bounds.
//設(shè)置leash的層為0
//leash將被放置在Z軸的最底層,如果有其他層級(jí)的SurfaceControl對(duì)象,它們將會(huì)覆蓋在leash之上。
t.setLayer(leash, 0);
//并設(shè)置AnimationBoundsLayer的層為上一個(gè)層的值,保證leash在AnimationBoundsLayer下面
t.setLayer(mAnimationBoundsLayer, getLastLayer());
// Reparent leash to animation bounds layer.
//重新將leash的父節(jié)點(diǎn)設(shè)置為動(dòng)畫(huà)邊界層。
t.reparent(leash, mAnimationBoundsLayer);
}
}
private SurfaceControl createAnimationBoundsLayer(Transaction t) {
ProtoLog.i(WM_DEBUG_APP_TRANSITIONS_ANIM, "Creating animation bounds layer");
final SurfaceControl.Builder builder = makeAnimationLeash()
//給AnimationBoundsLayer設(shè)置父節(jié)點(diǎn)為L(zhǎng)eash的父節(jié)點(diǎn)
//即把動(dòng)畫(huà)邊界層的父節(jié)點(diǎn)設(shè)置為DefaultTaskDsiplayArea
.setParent(getAnimationLeashParent())
.setName(getSurfaceControl() + " - animation-bounds")
.setCallsite("ActivityRecord.createAnimationBoundsLayer");
final SurfaceControl boundsLayer = builder.build();
t.show(boundsLayer);
return boundsLayer;
}
這個(gè)方法其實(shí)主要就是做了兩件事:
1.根據(jù)mAnimatingActivityRegistry的值判斷,是否需要把有動(dòng)畫(huà)效果的Activity添加到列表中
2.根據(jù)mNeedsAnimationBoundsLayer的值判斷,否需要?jiǎng)?chuàng)建一個(gè)動(dòng)畫(huà)邊界層
createAnimationBoundsLayer
就是創(chuàng)建了一個(gè)SurfaceControl。getLastLayer()
用于返回當(dāng)前窗口的最高(或最后)層級(jí)。假設(shè)我們有一個(gè)窗口管理系統(tǒng)中,窗口的層級(jí)從0開(kāi)始編號(hào)。當(dāng)一個(gè)新窗口創(chuàng)建時(shí),它可能被賦予層級(jí)0。然后,如果這個(gè)新窗口被另一個(gè)窗口覆蓋,那么新窗口的層級(jí)可能會(huì)更新為1,依此類推。
通過(guò)使用AnimationBoundsLayer,可以定義一個(gè)矩形區(qū)域,該區(qū)域可以作為動(dòng)畫(huà)的邊界。當(dāng)動(dòng)畫(huà)開(kāi)始時(shí),它只在該定義的區(qū)域內(nèi)顯示,不會(huì)超出這個(gè)邊界。AnimationBoundsLayer的主要作用是限制動(dòng)畫(huà)的顯示區(qū)域,以確保動(dòng)畫(huà)不會(huì)影響到應(yīng)用程序的其他部分。
將leash傳給RemoteAnimationAdapterWrapper
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
mAnimation
是AnimationAdapter接口的對(duì)象,調(diào)用其startAnimation
方法,傳遞mLeash(動(dòng)畫(huà))、t(事務(wù))、type(動(dòng)畫(huà)類型)和mInnerAnimationFinishedCallback(回調(diào)函數(shù))。mAnimation
的值是前面WindowContainer.startAnimation傳遞的anim
,這個(gè)anim
實(shí)際上就是傳遞的也就是前面WindowContainer.getAnimationAdapter中創(chuàng)建startBounds變量為空情況的RemoteAnimationAdapterWrapper對(duì)象。因此這里調(diào)用接口AnimationAdapter的方法startAnimation正是在RemoteAnimationAdapterWrapper中實(shí)現(xiàn)的。
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/RemoteAnimationController.java
class RemoteAnimationAdapterWrapper implements AnimationAdapter {
private final RemoteAnimationRecord mRecord;
SurfaceControl mCapturedLeash;
private OnAnimationFinishedCallback mCapturedFinishCallback;
private @AnimationType int mAnimationType;
......
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
//設(shè)置動(dòng)畫(huà)的起始位置和窗口裁剪區(qū)域
if (mStartBounds.isEmpty()) {
// Restore position and stack crop until client has a chance to modify it.
t.setPosition(animationLeash, mPosition.x, mPosition.y);
t.setWindowCrop(animationLeash, mEndBounds.width(), mEndBounds.height());
} else {
// Offset the change animation leash to the relative start position in parent.
// (mPosition) is the relative end position in parent container.
// (mStartBounds - mEndBounds) is the position difference between start and end.
// (mPosition + mStartBounds - mEndBounds) will be the relative start position.
t.setPosition(animationLeash, mPosition.x + mStartBounds.left - mEndBounds.left,
mPosition.y + mStartBounds.top - mEndBounds.top);
t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height());
}
//保存動(dòng)畫(huà)圖層到mCapturedLeash
mCapturedLeash = animationLeash;
//當(dāng)動(dòng)畫(huà)完成時(shí)的回調(diào)函數(shù) finishCallback保存到mCapturedFinishCallback。
mCapturedFinishCallback = finishCallback;
//保存動(dòng)畫(huà)類型
mAnimationType = type;
}
......
}
此時(shí)動(dòng)畫(huà)圖層到mCapturedLeash
;
動(dòng)畫(huà)完成時(shí)的回調(diào)函數(shù)保存到mCapturedFinishCallback
,mCapturedFinishCallback
是前面?zhèn)鬟f的mInnerAnimationFinishedCallback
,而mInnerAnimationFinishedCallback
在SurfaceAnimator的構(gòu)造方法初始化的值是getFinishedCallback(staticAnimationFinishedCallback)
,即動(dòng)畫(huà)完成時(shí)的回調(diào)mCapturedFinishCallback
對(duì)應(yīng)的就是getFinishedCallback(staticAnimationFinishedCallback)
,這一點(diǎn)和本地動(dòng)畫(huà)相同;
動(dòng)畫(huà)類型保存到了mAnimationType
。
這個(gè)方法的主要目的是根據(jù)提供的起始和結(jié)束邊界來(lái)設(shè)置動(dòng)畫(huà)的起始位置和窗口裁剪區(qū)域,并保存相關(guān)信息到RemoteAnimationAdapterWrapper的成員變量中,以供后續(xù)使用。
啟動(dòng)動(dòng)畫(huà)流程
layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
在最開(kāi)始的AppTransitionController.handleAppTransitionReady方法中調(diào)用AppTransition.goodToGo方法開(kāi)始啟動(dòng)并顯示動(dòng)畫(huà)的流程。
入?yún)⑼瑯佣际窃趆andleAppTransitionReady方法中初始化的:
參數(shù)transit
的值通過(guò)getTransitCompatType方法中獲取,是TRANSIT_OLD_WALLPAPER_CLOSE
(12);
參數(shù)topOpeningApp
指的是正在打開(kāi)的應(yīng)用列表 (mOpeningApps) 中的頂層應(yīng)用,即應(yīng)用Task對(duì)應(yīng)的ActivityRecord,也就是我們?cè)谧烂鎲?dòng)的應(yīng)用的ActivityRecord。
判斷是否需要重新進(jìn)行布局,RemoteAnimationController狀態(tài)
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java
/**
* @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another
* layout pass needs to be done
*/
int goodToGo(@TransitionOldType int transit, ActivityRecord topOpeningApp) {
mNextAppTransitionFlags = 0;
mNextAppTransitionRequests.clear();
setAppTransitionState(APP_STATE_RUNNING);
//如果topOpeningApp不為空,即應(yīng)用ActivityRecord不為空,
//那么獲取其ActivityRecord的容器,即應(yīng)用Task
final WindowContainer wc =
topOpeningApp != null ? topOpeningApp.getAnimatingContainer() : null;
//應(yīng)用ActivityRecord不為空,獲取AnimationAdapter,即RemoteAnimationAdapterWrapper
final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null;
//通知所有的AppTransitionListener,應(yīng)用過(guò)渡動(dòng)畫(huà)即將開(kāi)始
//根據(jù)其返回值判斷是否需要重新進(jìn)行布局
//我們這里所有的調(diào)用AppTransitionListener的redoLayout返回值都0
//即redoLayout返回值都0
int redoLayout = notifyAppTransitionStartingLocked(
AppTransition.isKeyguardGoingAwayTransitOld(transit),
AppTransition.isKeyguardOccludeTransitOld(transit),
topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
topOpeningAnim != null
? topOpeningAnim.getStatusBarTransitionsStartTime()
: SystemClock.uptimeMillis(),
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
//mRemoteAnimationController不為空
if (mRemoteAnimationController != null) {
mRemoteAnimationController.goodToGo(transit);
}
//如果mRemoteAnimationController為空
//transit值為TRANSIT_OLD_WALLPAPER_CLOSE
//topOpeningAnim,應(yīng)用ActivityRecord不為空時(shí)
else if ((isTaskOpenTransitOld(transit) || transit == TRANSIT_OLD_WALLPAPER_CLOSE)
&& topOpeningAnim != null) {
//shouldAttachNavBarToAppDuringTransition(),在動(dòng)畫(huà)過(guò)渡期間將導(dǎo)航欄附加到應(yīng)用程序
//getRecentsAnimationController(),RecentsAnimationController為空
if (mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
&& mService.getRecentsAnimationController() == null) {
//創(chuàng)建一個(gè)新的NavBarFadeAnimationController
final NavBarFadeAnimationController controller =
new NavBarFadeAnimationController(mDisplayContent);
// For remote animation case, the nav bar fades out and in is controlled by the
// remote side. For non-remote animation case, we play the fade out/in animation
// here. We play the nav bar fade-out animation when the app transition animation
// starts and play the fade-in animation sequentially once the fade-out is finished.
//使用fadeOutAndInSequentially方法使其淡出并淡入
controller.fadeOutAndInSequentially(topOpeningAnim.getDurationHint(),
null /* fadeOutParent */, topOpeningApp.getSurfaceControl());
}
}
return redoLayout;
}
我們RemoteAnimationController已經(jīng)創(chuàng)建,所以mRemoteAnimationController != null
為true,進(jìn)入該流程mRemoteAnimationController.goodToGo(transit);
,調(diào)用RemoteAnimationController的goodToGo方法,傳遞的參數(shù)值為TRANSIT_OLD_WALLPAPER_CLOSE
傳遞動(dòng)畫(huà)信息到桌面進(jìn)程
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/RemoteAnimationController.java
/**
* Called when the transition is ready to be started, and all leashes have been set up.
*/
void goodToGo(@WindowManager.TransitionOldType int transit) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
//如果動(dòng)畫(huà)已取消,進(jìn)入動(dòng)畫(huà)播放完成的流程
if (mCanceled) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
"goodToGo(): Animation canceled already");
onAnimationFinished();
invokeAnimationCancelled("already_cancelled");
return;
}
// Scale the timeout with the animator scale the controlling app is using.
//一個(gè)定時(shí)器,超過(guò)TIMEOUT_MS則執(zhí)行mTimeoutRunnable
//實(shí)際上就是執(zhí)行cancelAnimation方法取消動(dòng)畫(huà)
mHandler.postDelayed(mTimeoutRunnable,
(long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
//創(chuàng)建動(dòng)畫(huà)完成時(shí)的回調(diào)函數(shù)
mFinishedCallback = new FinishedCallback(this);
// Create the app targets
//創(chuàng)建類型為App的RemoteAnimationTarget
final RemoteAnimationTarget[] appTargets = createAppAnimations();
//判斷如果appTargets為空,且transit的值與鎖屏相關(guān)
//則取消動(dòng)畫(huà)播放進(jìn)入動(dòng)畫(huà)播放完成的流程
if (appTargets.length == 0 && !AppTransition.isKeyguardOccludeTransitOld(transit)) {
// Keyguard occlude transition can be executed before the occluding activity becomes
// visible. Even in this case, KeyguardService expects to receive binder call, so we
// don't cancel remote animation.
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
"goodToGo(): No apps to animate, mPendingAnimations=%d",
mPendingAnimations.size());
onAnimationFinished();
invokeAnimationCancelled("no_app_targets");
return;
}
//運(yùn)行mOnRemoteAnimationReady,做一些播放動(dòng)畫(huà)前的檢查
if (mOnRemoteAnimationReady != null) {
mOnRemoteAnimationReady.run();
mOnRemoteAnimationReady = null;
}
// Create the remote wallpaper animation targets (if any)
//創(chuàng)建壁紙類型的RemoteAnimationTarget
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
// Create the remote non app animation targets (if any)
//創(chuàng)建非app類型的RemoteAnimationTarget,例如狀態(tài)欄,導(dǎo)航欄等
final RemoteAnimationTarget[] nonAppTargets = createNonAppWindowAnimations(transit);
//添加動(dòng)畫(huà)啟動(dòng)后的回調(diào)
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
//監(jiān)聽(tīng)綁定IRemoteAnimationRunner的服務(wù)是否死亡
//綁定IRemoteAnimationRunner實(shí)際上就是LauncherAnimationRunner
linkToDeathOfRunner();
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): onAnimationStart,"
+ " transit=%s, apps=%d, wallpapers=%d, nonApps=%d",
AppTransition.appTransitionOldToString(transit), appTargets.length,
wallpaperTargets.length, nonAppTargets.length);
//進(jìn)入桌面進(jìn)程啟動(dòng)動(dòng)畫(huà)
mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
wallpaperTargets, nonAppTargets, mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
}
if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");
writeStartDebugStatement();
}
});
setRunningRemoteAnimation(true);
}
這里我們主要講類型為App的遠(yuǎn)程動(dòng)畫(huà)
創(chuàng)建類型為App的RemoteAnimationTarget
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
private RemoteAnimationTarget[] createAppAnimations() {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAppAnimations()");
//創(chuàng)建一個(gè) ArrayList 來(lái)存儲(chǔ) RemoteAnimationTarget 對(duì)象。
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
//mPendingAnimations存放的是RemoteAnimationRecord對(duì)象
//在前面createRemoteAnimationRecord方法中添加
//注意這里是從列表的末尾開(kāi)始遍歷,這通常是為了在移除元素時(shí)避免索引問(wèn)題。
//為桌面和應(yīng)用創(chuàng)建RemoteAnimationTarget
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
//獲取對(duì)應(yīng)的RemoteAnimationRecord對(duì)象
final RemoteAnimationRecord wrappers = mPendingAnimations.get(i);
//為RemoteAnimationRecord對(duì)象創(chuàng)建createRemoteAnimationTarget
final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget();
if (target != null) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tAdd container=%s",
wrappers.mWindowContainer);
//把創(chuàng)建的RemoteAnimationTarget對(duì)象存放到targets列表中
targets.add(target);
} else {
......
}
}
//返回一個(gè)RemoteAnimationTarget數(shù)組
return targets.toArray(new RemoteAnimationTarget[targets.size()]);
}
這個(gè)方法主要是為啟動(dòng)的應(yīng)用和桌面創(chuàng)建RemoteAnimationTarget對(duì)象,RemoteAnimationTarget用來(lái)保存前面創(chuàng)建的動(dòng)畫(huà)圖層、打開(kāi)還是關(guān)閉狀態(tài)的mode等信息。
下面我們看看RemoteAnimationTarget是如何創(chuàng)建的final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget();
從對(duì)應(yīng)的RemoteAnimationRecord中調(diào)用createRemoteAnimationTarget方法。這里mPendingAnimations從末尾開(kāi)始遍歷,因此先創(chuàng)建的是桌面的RemoteAnimationTarget,之后創(chuàng)建應(yīng)用的RemoteAnimationTarget。
RemoteAnimationTarget createRemoteAnimationTarget() {
if (mAdapter == null
|| mAdapter.mCapturedFinishCallback == null
|| mAdapter.mCapturedLeash == null) {
return null;
}
//調(diào)用對(duì)應(yīng)的createRemoteAnimationTarget方法
mTarget = mWindowContainer.createRemoteAnimationTarget(this);
return mTarget;
}
當(dāng)前不論是桌面還是應(yīng)用的容器都是Task,因此當(dāng)前mWindowContainer
是對(duì)應(yīng)的Task,而this
代表的是當(dāng)前對(duì)應(yīng)的RemoteAnimationRecord。
我們這里調(diào)用mWindowContainer.createRemoteAnimationTarget(this);
指的就是在Task類中重寫的createRemoteAnimationTarget方法,但是Task類中沒(méi)有重寫的createRemoteAnimationTarget方法,因此向上查找其父類,發(fā)現(xiàn)重寫的方法在TaskFragment中。
@Override
RemoteAnimationTarget createRemoteAnimationTarget(
RemoteAnimationController.RemoteAnimationRecord record) {
//根據(jù)當(dāng)前的RemoteAnimationRecord對(duì)象中的mMode值判斷activity的值
final ActivityRecord activity = record.getMode() == RemoteAnimationTarget.MODE_OPENING
// There may be a launching (e.g. trampoline or embedded) activity without a window
// on top of the existing task which is moving to front. Exclude finishing activity
// so the window of next activity can be chosen to create the animation target.
? getActivity(r -> !r.finishing && r.hasChild())
: getTopMostActivity();
//activity不為空就創(chuàng)建RemoteAnimationTarget
return activity != null ? activity.createRemoteAnimationTarget(record) : null;
}
桌面的RemoteAnimationRecord對(duì)象中的mMode值是MODE_CLOSING
,因此通過(guò)getActivity(r -> !r.finishing && r.hasChild())
獲取Activity;
應(yīng)用的RemoteAnimationRecord對(duì)象中的mMode值是MODE_OPENING
,因此通過(guò)getTopMostActivity()
方法獲取Activity。
這兩種獲取方式唯一的差異就是參數(shù)區(qū)別,代碼邏輯變化不大。
我們這里activity的值都不為空,因此最后調(diào)用ActivityRecord中的createRemoteAnimationTarget方法創(chuàng)建RemoteAnimationTarget。
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
@Override
RemoteAnimationTarget createRemoteAnimationTarget(
RemoteAnimationController.RemoteAnimationRecord record) {
final WindowState mainWindow = findMainWindow();
if (task == null || mainWindow == null) {
return null;
}
final Rect insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
task.getBounds(), Type.systemBars(), false /* ignoreVisibility */).toRect();
InsetUtils.addInsets(insets, getLetterboxInsets());
final RemoteAnimationTarget target = new RemoteAnimationTarget(task.mTaskId,
record.getMode(), record.mAdapter.mCapturedLeash, !fillsParent(),
new Rect(), insets,
getPrefixOrderIndex(), record.mAdapter.mPosition, record.mAdapter.mLocalBounds,
record.mAdapter.mEndBounds, task.getWindowConfiguration(),
false /*isNotInRecents*/,
record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null,
record.mStartBounds, task.getTaskInfo(), checkEnterPictureInPictureAppOpsState());
target.setShowBackdrop(record.mShowBackdrop);
target.setWillShowImeOnTarget(mStartingData != null && mStartingData.hasImeSurface());
target.hasAnimatingParent = record.hasAnimatingParent();
return target;
}
主要就是把RemoteAnimationRecord和RemoteAnimationAdapterWrapper中的一些參數(shù)保存到RemoteAnimationTarget中,其中最為關(guān)鍵動(dòng)畫(huà)圖層信息record.mAdapter.mCapturedLeash
保存到RemoteAnimationTarget對(duì)應(yīng)的變量leash
中。
進(jìn)入桌面進(jìn)程啟動(dòng)動(dòng)畫(huà)
回調(diào)goodToGo方法中
mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
wallpaperTargets, nonAppTargets, mFinishedCallback);
其中mRemoteAnimationAdapter.getRunner()
指的是IRemoteAnimationRunner對(duì)象,通過(guò)跨進(jìn)程通信到桌面進(jìn)程調(diào)用onAnimationStart方法。
代碼路徑:frameworks/base/core/java/android/view/IRemoteAnimationRunner.aidl
oneway interface IRemoteAnimationRunner {
/**
* Called when the process needs to start the remote animation.
*
* @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
* @param apps The list of apps to animate.
* @param wallpapers The list of wallpapers to animate.
* @param nonApps The list of non-app windows such as Bubbles to animate.
* @param finishedCallback The callback to invoke when the animation is finished.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void onAnimationStart(int transit, in RemoteAnimationTarget[] apps,
in RemoteAnimationTarget[] wallpapers, in RemoteAnimationTarget[] nonApps,
in IRemoteAnimationFinishedCallback finishedCallback);
......
}
oneway
類型接口,因此里面的方法都屬于異步binder調(diào)用。
這里傳遞的參數(shù)transit
的值是TRANSIT_OLD_WALLPAPER_CLOSE
(12);appTargets
指的是前面通過(guò)createAppAnimations方法中獲取的桌面和應(yīng)用的RemoteAnimationTarget;wallpaperTargets
通過(guò)前面createWallpaperAnimations方法中獲取的壁紙的RemoteAnimationTarget;nonAppTargets
通過(guò)前面createNonAppWindowAnimations方法中獲取的非APP類型的RemoteAnimationTarget;mFinishedCallback
前面mFinishedCallback = new FinishedCallback(this);
創(chuàng)建的FinishedCallback對(duì)象。
代碼路徑:frameworks/base/services/core/java/com/android/server/wm/RemoteAnimationController.java文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-844073.html
private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
RemoteAnimationController mOuter;
FinishedCallback(RemoteAnimationController outer) {
mOuter = outer;
}
......
}
實(shí)際上實(shí)現(xiàn)的就是IRemoteAnimationFinishedCallback,桌面處理完成動(dòng)畫(huà)后跨進(jìn)程回到系統(tǒng)進(jìn)程進(jìn)行回調(diào)。
后續(xù)流程見(jiàn)Android T 遠(yuǎn)程動(dòng)畫(huà)顯示流程其三文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-844073.html
到了這里,關(guān)于Android T 遠(yuǎn)程動(dòng)畫(huà)顯示流程其二——系統(tǒng)側(cè)動(dòng)畫(huà)啟動(dòng)流程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!