StartingWindow自定义动画setOnExitAnimationListener

111 阅读1分钟

StartingWindow要想进入自定义动画,需要在 com/android/server/wm/ActivityRecord.java

void removeStartingWindow() {
        boolean prevEligibleForLetterboxEducation = isEligibleForLetterboxEducation();
        
        if (transferSplashScreenIfNeeded()) {
            // 如果需要传递splashScreen到应用端,就返回了
            return;
        }
        removeStartingWindowAnimation(true /* prepareAnimation */);

        final Task task = getTask();
        if (prevEligibleForLetterboxEducation != isEligibleForLetterboxEducation()
                && task != null) {
            // Trigger TaskInfoChanged to update the letterbox education.
            task.dispatchTaskInfoChangedIfNeeded(true /* force */);
        }
    }

需要transferSplashScreenIfNeeded返回true,不然就走下面的老流程了。

com/android/server/wm/ActivityRecord.java

private boolean transferSplashScreenIfNeeded() {
        if (finishing || !mHandleExitSplashScreen || mStartingSurface == null
                || mStartingWindow == null
                || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
                // skip copy splash screen to client if it was resized
                || (mStartingData != null && mStartingData.mResizedFromTransfer)) {
            return false;
        }
        if (isTransferringSplashScreen()) {
            return true;
        }
        // Only do transfer after transaction has done when starting window exist.
        if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommit) {
            mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
            return true;
        }
        requestCopySplashScreen();
        return isTransferringSplashScreen();
    }

我们activity刚打开finishing不可能为true,mStartingSurface和mStartingWindow是startingwindow创建的时候赋值的不为空,mTransferringSplashScreenState一直没有操作过所以不可能为TRANSFER_SPLASH_SCREEN_FINISH,mResizedFromTransfer是app resize相关的,也不是true。所以关键就是mHandleExitSplashScreen

发现只有一个地方赋值的。 img_v3_02mn_a1e3520e-6e22-4c57-98a3-453d07d8632l.jpg

调用关系:

ActivityRecord#setCustomizeSplashScreenExitAnimation
    ActivityRecord#activityResumedLocked
        ActivityClientController#activityResumed
            ActivityClient#activityResumed
                ResumeActivityItem#postExecute

android/app/servertransaction/ResumeActivityItem.java

    public void postExecute(@NonNull ClientTransactionHandler client,
            @NonNull PendingTransactionActions pendingActions) {
        // TODO(lifecycler): Use interface callback instead of actual implementation.
        ActivityClient.getInstance().activityResumed(getActivityToken(),
                client.isHandleSplashScreenExit(getActivityToken()));
    }

就是对应client.isHandleSplashScreenExit(getActivityToken())的值,继续跟进

android/app/ActivityThread.java

    public boolean isHandleSplashScreenExit(@NonNull IBinder token) {
        synchronized (this) {
            return mSplashScreenGlobal != null && mSplashScreenGlobal.containsExitListener(token);
        }
    }

android/window/SplashScreen.java

        public boolean containsExitListener(IBinder token) {
            synchronized (mGlobalLock) {
                final SplashScreenImpl impl = findImpl(token);
                return impl != null && impl.mExitAnimationListener != null;
            }
        }

impl.mExitAnimationListener的赋值 android/window/SplashScreen.java#SplashScreenImpl

        public void setOnExitAnimationListener(
                @NonNull SplashScreen.OnExitAnimationListener listener) {
            if (mActivityToken == null) {
                // This is not an activity.
                return;
            }
            synchronized (mGlobal.mGlobalLock) {
                if (listener != null) {
                       // 这边赋值的
                    mExitAnimationListener = listener;
                    mGlobal.addImpl(this);
                }
            }
        }

这个setOnExitAnimationListener就是app侧设置的,参考官方文档 developer.android.com/develop/ui/…

getSplashScreen().setOnExitAnimationListener(splashScreenView -> {
        final ObjectAnimator slideUp = ObjectAnimator.ofFloat(
                splashScreenView,
                View.TRANSLATION_Y,
                0f,
                -splashScreenView.getHeight()
        );
        slideUp.setInterpolator(new AnticipateInterpolator());
        slideUp.setDuration(200L);
        slideUp.start();
    });

这边就清楚了,app侧调用getSplashScreen().setOnExitAnimationListener设置回调,会把systemServer侧的ActivityRecord的mHandleExitSplashScreen设置为true,这样transferSplashScreenIfNeeded就可以进行下去执行requestCopySplashScreen,后续详细流程可以看juejin.cn/post/750891…