转屏动画 - 安卓R

本文介绍安卓动画系统中的转屏动画流程。

1 旋转的开始

若当前屏幕方向需要旋转,会调用frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java的updateRotation方法:

    /**
     * Recalculate the current rotation.
     *
     * Called by the window manager policy whenever the state of the system changes
     * such that the current rotation might need to be updated, such as when the
     * device is docked or rotated into a new posture.
     */
    @Override
    public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
        updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
    }

    private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
        ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:"
                        + " alwaysSendConfiguration=%b forceRelayout=%b",
                alwaysSendConfiguration, forceRelayout);

        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");

        long origId = Binder.clearCallingIdentity();

        try {
            synchronized (mGlobalLock) {
                boolean layoutNeeded = false;
                final int displayCount = mRoot.mChildren.size();
                for (int i = 0; i < displayCount; ++i) {
                    final DisplayContent displayContent = mRoot.mChildren.get(i);
                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
                    final boolean rotationChanged = displayContent.updateRotationUnchecked();
                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

                    if (rotationChanged) {
                        mAtmService.getTaskChangeNotificationController()
                                .notifyOnActivityRotation(displayContent.mDisplayId);
                    }

                    if (!rotationChanged || forceRelayout) {
                        displayContent.setLayoutNeeded();
                        layoutNeeded = true;
                    }
                    if (rotationChanged || alwaysSendConfiguration) {
                        displayContent.sendNewConfiguration();
                    }
                }

                if (layoutNeeded) {
                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                            "updateRotation: performSurfacePlacement");
                    mWindowPlacerLocked.performSurfacePlacement();
                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

遍历在mRoot.mChildren中的每个DisplayContent(frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java),首先调用了其updateRotationUnchecked方法:

    /**
     * Update rotation of the display.
     *
     * @return {@code true} if the rotation has been changed.  In this case YOU MUST CALL
     *         {@link #sendNewConfiguration} TO UNFREEZE THE SCREEN.
     */
    boolean updateRotationUnchecked() {
        return mDisplayRotation.updateRotationUnchecked(false /* forceUpdate */);
    }

这里直接调用了frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java的updateRotationUnchecked方法:

    /**
     * Update rotation with an option to force the update. This updates the container's perception
     * of rotation and, depending on the top activities, will freeze the screen or start seamless
     * rotation. The display itself gets rotated in {@link DisplayContent#applyRotationLocked}
     * during {@link DisplayContent#sendNewConfiguration}.
     *
     * @param forceUpdate Force the rotation update. Sometimes in WM we might skip updating
     *                    orientation because we're waiting for some rotation to finish or display
     *                    to unfreeze, which results in configuration of the previously visible
     *                    activity being applied to a newly visible one. Forcing the rotation
     *                    update allows to workaround this issue.
     * @return {@code true} if the rotation has been changed. In this case YOU MUST CALL
     *         {@link DisplayContent#sendNewConfiguration} TO COMPLETE THE ROTATION AND UNFREEZE
     *         THE SCREEN.
     */
    boolean updateRotationUnchecked(boolean forceUpdate) {
        ......
        mDisplayContent.setLayoutNeeded();

        if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
            // The screen rotation animation uses a screenshot to freeze the screen while windows
            // resize underneath. When we are rotating seamlessly, we allow the elements to
            // transition to their rotated state independently and without a freeze required.
            prepareSeamlessRotation();
        } else {
            prepareNormalRotationAnimation();
        }

        // Give a remote handler (system ui) some time to reposition things.
        startRemoteRotation(oldRotation, mRotation);

        return true;
    }

这里通过DisplayRotation的shouldRotateSeamlessly方法判断是否可以无缝旋转:

    @VisibleForTesting
    boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
        // Display doesn't need to be frozen because application has been started in correct
        // rotation already, so the rest of the windows can use seamless rotation.
        if (mDisplayContent.hasTopFixedRotationLaunchingApp()) {
            return true;
        }

        final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
        if (w == null || w != mDisplayContent.mCurrentFocus) {
            return false;
        }
        // We only enable seamless rotation if the top window has requested it and is in the
        // fullscreen opaque state. Seamless rotation requires freezing various Surface states and
        // won't work well with animations, so we disable it in the animation case for now.
        if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) {
            return false;
        }

        // For the upside down rotation we don't rotate seamlessly as the navigation bar moves
        // position. Note most apps (using orientation:sensor or user as opposed to fullSensor)
        // will not enter the reverse portrait orientation, so actually the orientation won't change
        // at all.
        if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
            return false;
        }

        // If the navigation bar can't change sides, then it will jump when we change orientations
        // and we don't rotate seamlessly - unless that is allowed, eg. with gesture navigation
        // where the navbar is low-profile enough that this isn't very noticeable.
        if (!mAllowSeamlessRotationDespiteNavBarMoving && !mDisplayPolicy.navigationBarCanMove()) {
            return false;
        }

        // If the bounds of activity window is different from its parent, then reject to be seamless
        // because the window position may change after rotation that will look like a sudden jump.
        if (w.mActivityRecord != null && !w.mActivityRecord.matchParentBounds()) {
            return false;
        }

        // In the presence of the PINNED stack or System Alert windows we unfortunately can not
        // seamlessly rotate.
        if (mDisplayContent.getDefaultTaskDisplayArea().hasPinnedTask()
                || mDisplayContent.hasAlertWindowSurfaces()) {
            return false;
        }

        // We can't rotate (seamlessly or not) while waiting for the last seamless rotation to
        // complete (that is, waiting for windows to redraw). It's tempting to check
        // mSeamlessRotationCount but that could be incorrect in the case of window-removal.
        if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
            return false;
        }

        return true;
    }

2 旋转方法

2.1 无缝旋转

若能无缝旋转,则调用了DisplayRotation的prepareSeamlessRotation方法:

    private void prepareSeamlessRotation() {
        // We are careful to reset this in case a window was removed before it finished
        // seamless rotation.
        mSeamlessRotationCount = 0;
        mRotatingSeamlessly = true;
    }

2.2 冻屏旋转

若不能无缝旋转,则调用了DisplayRotation的prepareNormalRotationAnimation方法:

    void prepareNormalRotationAnimation() {
        cancelSeamlessRotation();
        final RotationAnimationPair anim = selectRotationAnimation();
        mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);
    }

在这里调用了frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java的startFreezingDisplay方法进行冻屏:

    void startFreezingDisplay(int exitAnim, int enterAnim) {
        startFreezingDisplay(exitAnim, enterAnim, getDefaultDisplayContentLocked());
    }

    void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent) {
        startFreezingDisplay(exitAnim, enterAnim, displayContent,
                ROTATION_UNDEFINED /* overrideOriginalRotation */);
    }

    void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
            int overrideOriginalRotation) {
        ......
        displayContent.setRotationAnimation(new ScreenRotationAnimation(displayContent,
                originalRotation));
    }

ScreenRotationAnimation在frameworks/base/services/core/java/com/android/server/wm/ScreenRotationAnimation.java中实现,其构造函数:

    ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) {
        ......
        // Check whether the current screen contains any secure content.
        final boolean isSecure = displayContent.hasSecureWindowOnScreen();
        final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
        try {
            mBackColorSurface = displayContent.makeChildSurface(null)
                    .setName("BackColorSurface")
                    .setColorLayer()
                    .setCallsite("ScreenRotationAnimation")
                    .build();

            mScreenshotLayer = displayContent.makeOverlay()
                    .setName("RotationLayer")
                    .setBufferSize(mWidth, mHeight)
                    .setSecure(isSecure)
                    .setCallsite("ScreenRotationAnimation")
                    .build();

            mEnterBlackFrameLayer = displayContent.makeOverlay()
                    .setName("EnterBlackFrameLayer")
                    .setContainerLayer()
                    .setCallsite("ScreenRotationAnimation")
                    .build();

            // In case display bounds change, screenshot buffer and surface may mismatch so set a
            // scaling mode.
            SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();
            t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW);
            t2.apply(true /* sync */);

            // Capture a screenshot into the surface we just created.
            final int displayId = displayContent.getDisplayId();
            final Surface surface = mService.mSurfaceFactory.get();
            surface.copyFrom(mScreenshotLayer);
            SurfaceControl.ScreenshotGraphicBuffer gb =
                    mService.mDisplayManagerInternal.systemScreenshot(displayId);
            if (gb != null) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                        "ScreenRotationAnimation#getMedianBorderLuma");
                mStartLuma = RotationAnimationUtils.getMedianBorderLuma(gb.getGraphicBuffer(),
                        gb.getColorSpace());
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                try {
                    surface.attachAndQueueBufferWithColorSpace(gb.getGraphicBuffer(),
                            gb.getColorSpace());
                } catch (RuntimeException e) {
                    Slog.w(TAG, "Failed to attach screenshot - " + e.getMessage());
                }
                // If the screenshot contains secure layers, we have to make sure the
                // screenshot surface we display it in also has FLAG_SECURE so that
                // the user can not screenshot secure layers via the screenshot surface.
                if (gb.containsSecureLayers()) {
                    t.setSecure(mScreenshotLayer, true);
                }
                t.setLayer(mScreenshotLayer, SCREEN_FREEZE_LAYER_BASE);
                t.reparent(mBackColorSurface, displayContent.getSurfaceControl());
                t.setLayer(mBackColorSurface, -1);
                t.setColor(mBackColorSurface, new float[]{mStartLuma, mStartLuma, mStartLuma});
                t.setAlpha(mBackColorSurface, 1);
                t.show(mScreenshotLayer);
                t.show(mBackColorSurface);
            } else {
                Slog.w(TAG, "Unable to take screenshot of display " + displayId);
            }
            surface.destroy();
        } catch (OutOfResourcesException e) {
            Slog.w(TAG, "Unable to allocate freeze surface", e);
        }

        ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
                    "  FREEZE %s: CREATE", mScreenshotLayer);
        setRotation(t, realOriginalRotation);
        t.apply();
    }

在这里对屏幕进行了截屏,保存在mScreenshotLayer中。

3 旋转动画

接下来回到WindowManagerService的updateRotation方法中,还调用了displayContent(frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java)的sendNewConfiguration方法:

    void sendNewConfiguration() {
        ......
        // Something changed (E.g. device rotation), but no configuration update is needed.
        // E.g. changing device rotation by 180 degrees. Go ahead and perform surface placement to
        // unfreeze the display since we froze it when the rotation was updated in
        // DisplayContent#updateRotationUnchecked.
        if (mWaitingForConfig) {
            mWaitingForConfig = false;
            mWmService.mLastFinishedFreezeSource = "config-unchanged";
            setLayoutNeeded();
            mWmService.mWindowPlacerLocked.performSurfacePlacement();
        }
    }

mWmService.mWindowPlacerLocked是frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java,调用了其performSurfacePlacement方法进入窗口布局流程,会调用frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java的performSurfacePlacementNoTrace方法:

    // "Something has changed!  Let's make it correct now."
    // TODO: Super crazy long method that should be broken down...
    void performSurfacePlacementNoTrace() {
        ......
        if (mOrientationChangeComplete) {
            if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
                mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
                mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
            }
            mWmService.stopFreezingDisplayLocked();
        }
        ......
    }

其中调用了WindowManagerService的stopFreezingDisplayLocked方法:

    void stopFreezingDisplayLocked() {
        ......
        ScreenRotationAnimation screenRotationAnimation = displayContent == null ? null
                : displayContent.getRotationAnimation();
        if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
            ProtoLog.i(WM_DEBUG_ORIENTATION, "**** Dismissing screen rotation animation");
            DisplayInfo displayInfo = displayContent.getDisplayInfo();
            // Get rotation animation again, with new top window
            if (!displayContent.getDisplayRotation().validateRotationAnimation(
                    mExitAnimId, mEnterAnimId, false /* forceDefault */)) {
                mExitAnimId = mEnterAnimId = 0;
            }
            if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
                    getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                        displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
                mTransaction.apply();
            } else {
                screenRotationAnimation.kill();
                displayContent.setRotationAnimation(null);
                updateRotation = true;
            }
        } else {
            if (screenRotationAnimation != null) {
                screenRotationAnimation.kill();
                displayContent.setRotationAnimation(null);
            }
            updateRotation = true;
        }
        ......
    }

其中调用了ScreenRotationAnimation的dismiss方法:

    /**
     * Returns true if animating.
     */
    public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
            float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
        if (mScreenshotLayer == null) {
            // Can't do animation.
            return false;
        }
        if (!mStarted) {
            mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(),
                    mDisplayContent.getWindowingLayer());
            startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
                    exitAnim, enterAnim);
            if (mPerf != null && !mIsPerfLockAcquired) {
                mPerf.perfHint(BoostFramework.VENDOR_HINT_ROTATION_ANIM_BOOST, null);
                mIsPerfLockAcquired = true;
            }
        }
        if (!mStarted) {
            return false;
        }
        mFinishAnimReady = true;
        return true;
    }

又调用了ScreenRotationAnimation的startAnimation方法:

    /**
     * Returns true if animating.
     */
    private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
            float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
        ......
        mStarted = true;

        // Figure out how the screen has moved from the original rotation.
        int delta = DisplayContent.deltaRotation(mCurRotation, mOriginalRotation);


        final boolean customAnim;
        if (exitAnim != 0 && enterAnim != 0) {
            customAnim = true;
            mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim);
            mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim);
            mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
                    R.anim.screen_rotate_alpha);
        } else {
            customAnim = false;
            switch (delta) { /* Counter-Clockwise Rotations */
                case Surface.ROTATION_0:
                    mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
                            R.anim.screen_rotate_0_exit);
                    mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
                            R.anim.screen_rotate_0_enter);
                    break;
                case Surface.ROTATION_90:
                    mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
                            R.anim.screen_rotate_plus_90_exit);
                    mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
                            R.anim.screen_rotate_plus_90_enter);
                    break;
                case Surface.ROTATION_180:
                    mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
                            R.anim.screen_rotate_180_exit);
                    mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
                            R.anim.screen_rotate_180_enter);
                    break;
                case Surface.ROTATION_270:
                    mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
                            R.anim.screen_rotate_minus_90_exit);
                    mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
                            R.anim.screen_rotate_minus_90_enter);
                    break;
            }
        }
        ......
        if (customAnim) {
            mSurfaceRotationAnimationController.startCustomAnimation();
        } else {
            mSurfaceRotationAnimationController.startScreenRotationAnimation();
        }

        return true;
    }

这里首先通过AnimationUtils的loadAnimation方法加载动画,然后调用了ScreenRotationAnimation的内部类SurfaceRotationAnimationController的startCustomAnimation或startScreenRotationAnimation方法:

        void startCustomAnimation() {
            try {
                mService.mSurfaceAnimationRunner.deferStartingAnimations();
                mRotateScreenAnimator = startScreenshotAlphaAnimation();
                mDisplayAnimator = startDisplayRotation();
                if (mEnteringBlackFrame != null) {
                    mEnterBlackFrameAnimator = startEnterBlackFrameAnimation();
                }
            } finally {
                mService.mSurfaceAnimationRunner.continueStartingAnimations();
            }
        }

        /**
         * Start the rotation animation of the display and the screenshot on the
         * {@link SurfaceAnimationRunner}.
         */
        void startScreenRotationAnimation() {
            try {
                mService.mSurfaceAnimationRunner.deferStartingAnimations();
                mDisplayAnimator = startDisplayRotation();
                mScreenshotRotationAnimator = startScreenshotRotationAnimation();
                startColorAnimation();
            } finally {
                mService.mSurfaceAnimationRunner.continueStartingAnimations();
            }
        }

这里面有许多startXXXAnimation方法,它们都会调用SurfaceRotationAnimationController的startAnimation方法:

        /**
         * Start an animation defined by animationSpec on a new {@link SurfaceAnimator}.
         *
         * @param animatable The animatable used for the animation.
         * @param animationSpec The spec of the animation.
         * @param animationFinishedCallback Callback passed to the {@link SurfaceAnimator}
         *                                    and called when the animation finishes.
         * @return The newly created {@link SurfaceAnimator} that as been started.
         */
        private SurfaceAnimator startAnimation(
                SurfaceAnimator.Animatable animatable,
                LocalAnimationAdapter.AnimationSpec animationSpec,
                OnAnimationFinishedCallback animationFinishedCallback) {
            SurfaceAnimator animator = new SurfaceAnimator(
                    animatable, animationFinishedCallback, mService);

            LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
                    animationSpec, mService.mSurfaceAnimationRunner);
            animator.startAnimation(mDisplayContent.getPendingTransaction(),
                    localAnimationAdapter, false, ANIMATION_TYPE_SCREEN_ROTATION);
            return animator;
        }

在这里首先创建了一个frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java,然后调用了其startAnimation方法开始动画。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SSSxCCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值