在Activity的Theme里设置关闭动画不生效的问题

遇到一个需求,当前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

你可能感兴趣的:(Android,AOSP)