遇到一个需求,当前Activity被栈下面一个singleTask的Activity顶出栈时,设置的关闭动画(android:activityCloseExitAnimation)不生效,类似于下面这样设置的:
然后把Activity的Theme设置为 android:theme="@style/BActivity" ,关闭或返回的时候没有生效。
手上正好有台Google手机,就编译了一套打印WindowManager日志的系统,来查看原因(以下基于Android8.1,最新的10的代码有些变更)。
动画是在AppTransition.java加载的,记住行号,1613:
https://cs.android.com/android/platform/superproject/+/android-8.1.0_r1:frameworks/base/services/core/java/com/android/server/wm/AppTransition.java;bpv=0;bpt=1;l=1613
加载动画,是通过一个WindowManager.LayoutParams的windowAnimations变量读出来的。但这个lp到底是不是我们Activity的呢,于是向上一直找,找到了lp的来源
https://cs.android.com/android/platform/superproject/+/android-8.1.0_r1:frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java;l=296
// Do a first pass through the tokens for two things:
// (1) Determine if both the closing and opening app token sets are wallpaper targets, in
// which case special animations are needed (since the wallpaper needs to stay static behind
// them).
// (2) Find the layout params of the top-most application window in the tokens, which is
// what will control the animation theme.
final int closingAppsCount = mService.mClosingApps.size();
appsCount = closingAppsCount + mService.mOpeningApps.size();
for (i = 0; i < appsCount; i++) {
final AppWindowToken wtoken;
if (i < closingAppsCount) {
wtoken = mService.mClosingApps.valueAt(i);
if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
closingAppHasWallpaper = true;
}
} else {
wtoken = mService.mOpeningApps.valueAt(i - closingAppsCount);
if (wallpaperTarget != null && wtoken.windowsCanBeWallpaperTarget()) {
openingAppHasWallpaper = true;
}
}
voiceInteraction |= wtoken.mVoiceInteraction;
if (wtoken.fillsParent()) {
final WindowState ws = wtoken.findMainWindow();
if (ws != null) {
animLp = ws.mAttrs;
bestAnimLayer = ws.mLayer;
fullscreenAnim = true;
}
} else if (!fullscreenAnim) {
final WindowState ws = wtoken.findMainWindow();
if (ws != null) {
if (ws.mLayer > bestAnimLayer) {
animLp = ws.mAttrs;
bestAnimLayer = ws.mLayer;
}
}
}
}
transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
closingAppHasWallpaper);
// 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 the lock screen.
if (!mService.mPolicy.allowAppAnimationsLw()) {
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"Animations disallowed by keyguard or dream.");
animLp = null;
}
processApplicationsAnimatingInPlace(transit);
mTmpLayerAndToken.token = null;
handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);
可以看到,这是一个所有closingApps和openingApps的集合。而opening排在closing之后……我们的activity实际上处于closing状态,所以会被opening最后一个覆盖。
因此可以得出结论,我们最终展示出来的Activity关闭动画,是留在屏幕上的那个Activity规定的动画(最后一个opening)……
解决方法(不是很完美,有时候还是不按照设定执行,但大部分时候好用):
只能在onPause()里检测一下如果处于isFinishing,则使用overridePendingTransition(int enterAnim, int exitAnim)方法。
他的原理是在AppTransition中,设置成员变量,这样就避免了读取新Activity的LayoutParams的动画。在读取xml动画(1613行)之前判断(1547行)……
https://cs.android.com/android/platform/superproject/+/android-8.1.0_r1:frameworks/base/services/core/java/com/android/server/wm/AppTransition.java;bpv=0;bpt=1;l=1547