Android WMS動(dòng)畫系統(tǒng)初探(三)

基于AndroidR源碼分析

Android WMS動(dòng)畫系統(tǒng)初探(一)

Android WMS動(dòng)畫系統(tǒng)初探(二)

Android WMS動(dòng)畫系統(tǒng)初探(三)

Android WMS動(dòng)畫系統(tǒng)初探 完結(jié)篇

屏幕旋轉(zhuǎn)動(dòng)畫

OrientationListener#onProposedRotationChanged ->
WindowManagerService#updateRotation

當(dāng)屏幕需要旋轉(zhuǎn),WindowOrientationListener響應(yīng)sensorchanged脖母,會(huì)調(diào)用到WMS的updateRotation方法画舌。
從這里開啟屏幕旋轉(zhuǎn)動(dòng)畫的入口。

WindowManagerService#updateRotation

    /**
     * 重新計(jì)算當(dāng)前旋轉(zhuǎn)
     * 當(dāng)系統(tǒng)狀態(tài)發(fā)生變化時(shí)摔竿,如當(dāng)前旋轉(zhuǎn)可能需要更新疲眷、
     * 例如當(dāng)設(shè)備头靠或旋轉(zhuǎn)到新的姿態(tài)時(shí)锈候,調(diào)用WindowManagerPolicy
     */
    @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");
                    // 獲取rotationChanged薄料, 如果是true
                    // 則必須調(diào)用DisplayContent#sendNewConfiguration完成屏幕旋轉(zhuǎn)或解凍
                    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,調(diào)用displayContent.updateRotationUnchecked()
-> DisplayRotation.updateRotationUnchecked

DisplayRotation#updateRotationUnchecked

    /**
     * 在這個(gè)方法中將通知container對(duì)于rotation的感知
     * 然后根據(jù)頂部的activity開始凍屏動(dòng)畫或者無縫動(dòng)畫 
     * The display itself gets rotated in {@link DisplayContent#applyRotationLocked}
     * during {@link DisplayContent#sendNewConfiguration}.
     *
     * @param forceUpdate 強(qiáng)制更新旋轉(zhuǎn)泵琳。
     * 有時(shí)在WM中摄职,我們可能會(huì)跳過更新方向,因?yàn)槲覀冋诘却D(zhuǎn)完成或顯示解凍
     * 這導(dǎo)致先前可見的活動(dòng)的配置被應(yīng)用到一個(gè)新可見的活動(dòng)获列。
     * 強(qiáng)制旋轉(zhuǎn)更新可以解決這個(gè)問題谷市。
     * @return {@code true} 如果是true
     * 則必須調(diào)用DisplayContent#sendNewConfiguration完成屏幕旋轉(zhuǎn)或解凍
     */
    boolean updateRotationUnchecked(boolean forceUpdate) {
        final int displayId = mDisplayContent.getDisplayId();
        if (!forceUpdate) {
            if (mDeferredRotationPauseCount > 0) {
                // rotation更新被暫停,推遲更新
                ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, rotation is paused.");
                return false;
            }

            final ScreenRotationAnimation screenRotationAnimation =
                    mDisplayContent.getRotationAnimation();
            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
                // 當(dāng)之前的旋轉(zhuǎn)動(dòng)畫仍在進(jìn)行時(shí)击孩,不能執(zhí)行旋轉(zhuǎn)更新
                // 將嘗試在動(dòng)畫完成和顯示解凍后再次更新迫悠。
                ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, animation in progress.");
                return false;
            }
            if (mService.mDisplayFrozen) {
                // 即使屏幕旋轉(zhuǎn)動(dòng)畫已經(jīng)完成(例如isAnimating返回false)
                // 但仍然有一段時(shí)間我們還沒有解凍顯示,我們也要停止旋轉(zhuǎn)巩梢。
                ProtoLog.v(WM_DEBUG_ORIENTATION,
                        "Deferring rotation, still finishing previous rotation");
                return false;
            }

            if (mDisplayContent.mFixedRotationTransitionListener
                    .isTopFixedOrientationRecentsAnimating()) {
                // 特殊場(chǎng)景:用戶在Recents動(dòng)畫運(yùn)行時(shí)旋轉(zhuǎn)屏幕创泄,忽略這次更新
                return false;
            }
        }

        if (!mService.mDisplayEnabled) {
            // display不可用場(chǎng)景
            ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, display is not enabled.");
            return false;
        }

        final int oldRotation = mRotation;
        final int lastOrientation = mLastOrientation;
        final int rotation = rotationForOrientation(lastOrientation, oldRotation);
        ProtoLog.v(WM_DEBUG_ORIENTATION,
                "Computed rotation=%s (%d) for display id=%d based on lastOrientation=%s (%d) and "
                        + "oldRotation=%s (%d)",
                Surface.rotationToString(rotation), rotation,
                displayId,
                ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,
                Surface.rotationToString(oldRotation), oldRotation);

        ProtoLog.v(WM_DEBUG_ORIENTATION,
                "Display id=%d selected orientation %s (%d), got rotation %s (%d)", displayId,
                ActivityInfo.screenOrientationToString(lastOrientation), lastOrientation,
                Surface.rotationToString(rotation), rotation);

        if (oldRotation == rotation) {
            // No change.
            return false;
        }

        ProtoLog.v(WM_DEBUG_ORIENTATION,
                "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
                        displayId, rotation, oldRotation, lastOrientation);

        if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
            mDisplayContent.mWaitingForConfig = true;
        }

        mRotation = rotation;

        mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
        mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
                mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);

        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;
    }

方法注釋很清楚地解釋了這個(gè)方法的工作:

  • 在這個(gè)方法中將通知container對(duì)于rotation的感知,然后根據(jù)頂部的activity開始凍屏動(dòng)畫或者無縫動(dòng)畫且改。
  • shouldRotateSeamlessly方法判斷是否需要準(zhǔn)備無縫動(dòng)畫验烧。
  • startRemoteRotation使得在動(dòng)畫開始之前systemui有機(jī)會(huì)響應(yīng)方向變化

    @DisplayContent.updateRotationUnchecked → @DisplayRotation.startRemoteRotation

    SystemUI端處理查看DisplayChangeController.onRotateDisplay

DisplayRotation#shouldRotateSeamlessly

   boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) {
        // 如果應(yīng)用(top activity)已經(jīng)在正確的方向上啟動(dòng)那么不需要凍結(jié)顯示
        // 剩下的窗口可以使用無縫旋轉(zhuǎn)
        if (mDisplayContent.hasTopFixedRotationLaunchingApp()) {
            return true;
        }

        final WindowState w = mDisplayPolicy.getTopFullscreenOpaqueWindow();
        if (w == null || w != mDisplayContent.mCurrentFocus) {
            return false;
        }
        // 只有當(dāng)top window請(qǐng)求了無縫旋轉(zhuǎn)并且處于全屏不透明狀態(tài)時(shí),我們才會(huì)啟用無縫旋轉(zhuǎn)又跛。
        // 無縫旋轉(zhuǎn)需要凍結(jié)各種表面狀態(tài),不適合動(dòng)畫若治,所以我們現(xiàn)在在動(dòng)畫情況下禁用它
        if (w.getAttrs().rotationAnimation != ROTATION_ANIMATION_SEAMLESS || w.isAnimatingLw()) {
            return false;
        }

        // 對(duì)于上下旋轉(zhuǎn)慨蓝,我們不會(huì)隨著導(dǎo)航條移動(dòng)位置而無縫地旋轉(zhuǎn)感混。
        // 注意大多數(shù)應(yīng)用程序(orientation:sensor|user,而不是fullSensor的應(yīng)用)
        // 不會(huì)進(jìn)入reverse portarit(豎屏的反向)狀態(tài)礼烈,所以實(shí)際上方向不會(huì)改變弧满。
        if (oldRotation == mUpsideDownRotation || newRotation == mUpsideDownRotation) {
            return false;
        }

        // 如果導(dǎo)航條不能改變side,那么當(dāng)我們改變方向時(shí)會(huì)發(fā)生跳躍庭呜,不能無縫地旋轉(zhuǎn)
        if (!mAllowSeamlessRotationDespiteNavBarMoving && !mDisplayPolicy.navigationBarCanMove()) {
            return false;
        }

        // 如果Activity的bounds不同于它的父窗口犀忱,那么也不能無縫,因?yàn)榇翱谖恢每赡軙?huì)在旋轉(zhuǎn)后改變
        if (w.mActivityRecord != null && !w.mActivityRecord.matchParentBounds()) {
            return false;
        }

        // 在存在PinnedTask或System Alert窗口時(shí)阴汇,無法無縫旋轉(zhuǎn)
        if (mDisplayContent.getDefaultTaskDisplayArea().hasPinnedTask()
                || mDisplayContent.hasAlertWindowSurfaces()) {
            return false;
        }

        // 在等待最后一次無縫旋轉(zhuǎn)完成(即等待窗口重繪)時(shí)数冬,不能旋轉(zhuǎn)(無縫或不無縫)搀庶。
        if (!forceUpdate && mDisplayContent.getWindow(win -> win.mSeamlesslyRotated) != null) {
            return false;
        }

        return true;
    }

可以看到進(jìn)行無縫旋轉(zhuǎn)的條件限制還是很多的,具體的判斷邏輯我已經(jīng)在代碼上做了注釋哥倔。

部分場(chǎng)景我目前還無法理解秸架,可能要在之后的開發(fā)維護(hù)工作中逐步理解清楚。

無縫旋轉(zhuǎn)

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

額...看起來無縫旋轉(zhuǎn)的prepare方法里并沒有做什么太多的事情

先看一下凍屏旋轉(zhuǎn)吧

凍屏旋轉(zhuǎn)

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

先是取消無縫旋轉(zhuǎn)動(dòng)畫东抹,然后進(jìn)行凍屏旋轉(zhuǎn)

    private static class RotationAnimationPair {
        @AnimRes
        int mEnter;
        @AnimRes
        int mExit;
    }

DisplayRotation#selectRotationAnimation方法挑選實(shí)際要運(yùn)行的RotationAnimationPair動(dòng)畫效果

WindowManagerService#startFreezingDisplay

    void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
            int overrideOriginalRotation) {
        // 如果當(dāng)前已經(jīng)處于凍屏或者無縫動(dòng)畫 return
        if (mDisplayFrozen || displayContent.getDisplayRotation().isRotatingSeamlessly()) {
            return;
        }

        // 判斷是否滿足凍屏條件
        if (!displayContent.isReady() || !mPolicy.isScreenOn() || !displayContent.okToAnimate()) {
            // No need to freeze the screen before the display is ready,  if the screen is off,
            // or we can't currently animate.
            return;
        }

        ProtoLog.d(WM_DEBUG_ORIENTATION,
                            "startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s",
                            exitAnim, enterAnim, Debug.getCallers(8));
        mScreenFrozenLock.acquire();

        mDisplayFrozen = true;
        mDisplayFreezeTime = SystemClock.elapsedRealtime();
        mLastFinishedFreezeSource = null;

        // {@link mDisplayFrozen} prevents us from freezing on multiple displays at the same time.
        // As a result, we only track the display that has initially froze the screen.
        mFrozenDisplayId = displayContent.getDisplayId();

        // 凍結(jié)輸入事件分發(fā)
        mInputManagerCallback.freezeInputDispatchingLw();

        if (displayContent.mAppTransition.isTransitionSet()) {
            displayContent.mAppTransition.freeze();
        }

        if (PROFILE_ORIENTATION) {
            File file = new File("/data/system/frozen");
            Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
        }

        mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
        mExitAnimId = exitAnim;
        mEnterAnimId = enterAnim;

        displayContent.updateDisplayInfo();
        // 這時(shí)候還未更新rotaton府阀,獲取的是旋轉(zhuǎn)前的rotation
        final int originalRotation = overrideOriginalRotation != ROTATION_UNDEFINED
                ? overrideOriginalRotation
                : displayContent.getDisplayInfo().rotation;
        // 創(chuàng)建ScreenRotationAnimation
        displayContent.setRotationAnimation(new ScreenRotationAnimation(displayContent,
                originalRotation));
    }

startFreezingDisplay構(gòu)建了ScreenRotationAnimation并設(shè)置到DisplayContent中

ScreenRotationAnimation

    ScreenRotationAnimation(DisplayContent displayContent, @Surface.Rotation int originalRotation) {
        mService = displayContent.mWmService;
        mContext = mService.mContext;
        mDisplayContent = displayContent;
        displayContent.getBounds(mOriginalDisplayRect);

        // Screenshot does NOT include rotation!
        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
        // 重新從DisplayInfo中獲取原方向值
        final int realOriginalRotation = displayInfo.rotation;
        final int originalWidth;
        final int originalHeight;
        // 這一段是獲取旋轉(zhuǎn)前的長(zhǎng)和寬
        if (displayContent.getDisplayRotation().isFixedToUserRotation()) {
            // Emulated orientation.
            mForceDefaultOrientation = true;
            originalWidth = displayContent.mBaseDisplayWidth;
            originalHeight = displayContent.mBaseDisplayHeight;
        } else {
            // Normal situation
            originalWidth = displayInfo.logicalWidth;
            originalHeight = displayInfo.logicalHeight;
        }
        if (realOriginalRotation == Surface.ROTATION_90
                || realOriginalRotation == Surface.ROTATION_270) {
            mWidth = originalHeight;
            mHeight = originalWidth;
        } else {
            mWidth = originalWidth;
            mHeight = originalHeight;
        }

        mOriginalRotation = originalRotation;

        // 計(jì)算旋轉(zhuǎn)前后delta值 用于旋轉(zhuǎn)動(dòng)畫效果
        final int delta = DisplayContent.deltaRotation(originalRotation, realOriginalRotation);
        final boolean flipped = delta == Surface.ROTATION_90 || delta == Surface.ROTATION_270;
        mOriginalWidth = flipped ? originalHeight : originalWidth;
        mOriginalHeight = flipped ? originalWidth : originalHeight;
        mSurfaceRotationAnimationController = new SurfaceRotationAnimationController();

        // Check whether the current screen contains any secure content.
        final boolean isSecure = displayContent.hasSecureWindowOnScreen();
        final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
        try {
            // 創(chuàng)建動(dòng)畫要用的幾個(gè)surface
            mBackColorSurface = displayContent.makeChildSurface(null)
                    .setName("BackColorSurface")
                    .setColorLayer()
                    .setCallsite("ScreenRotationAnimation")
                    .build();

            // mScreenshotLayer是真正用于旋轉(zhuǎn)動(dòng)畫的layer
            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 */);

            // 這一大段就是抓取截圖綁定到mScreenshotLayer
            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);
        // 這里我的理解是對(duì)rotationLayer做默認(rèn)的旋轉(zhuǎn)變換
        setRotation(t, realOriginalRotation);
        t.apply();
    }

接下來我們回到入口的 WindowManagerService#updateRotationUnchecked寞蚌,接下來繼續(xù)調(diào)用DisplayContent#sendNewConfiguration()

DisplayContent#sendNewConfiguration()

    void sendNewConfiguration() {
        ... 
        if (mWaitingForConfig) {
            mWaitingForConfig = false;
            mWmService.mLastFinishedFreezeSource = "config-unchanged";
            setLayoutNeeded();
            mWmService.mWindowPlacerLocked.performSurfacePlacement();
        }
    }

這里再次調(diào)用我們熟悉的performSurfacePlacement方法挟秤,經(jīng)典再現(xiàn),進(jìn)入窗口布局流程艘刚,
這個(gè)流程會(huì)像前文分析的那樣調(diào)用到RootWindowContainer#performSurfacePlacementNoTrace方法

    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();
        }
    }

mWmService#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,
                    // MIUI ADD: START
                    // getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                    mIsCastMode ? 0.0f : getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                    // END
                        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;
        }

displayContent.getRotationAnimation()獲取了前面流程設(shè)置的ScreenRotationAnimation
screenRotationAnimation.dismiss方法將調(diào)用startAnimation啟動(dòng)動(dòng)畫

ScreenRotationAnimation#startAnimation

    /**
     * Returns true if animating.
     */
    private boolean startAnimation(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) {
            return true;
        }

        mStarted = true;

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

        // 這里的exitAnim和enterAnim來自前面流程的selectRotationAnimation方法
        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 {
            // 如果selectRotationAnimation沒有選擇到custom動(dòng)畫效果 
            // 就加載默認(rèn)的對(duì)應(yīng)4種方向變換的動(dòng)畫效果
            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;
            }
        }

        ProtoLog.d(WM_DEBUG_ORIENTATION, "Start rotation animation. customAnim=%s, "
                        + "mCurRotation=%s, mOriginalRotation=%s",
                customAnim, Surface.rotationToString(mCurRotation),
                Surface.rotationToString(mOriginalRotation));

        mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
        mRotateExitAnimation.restrictDuration(maxAnimationDuration);
        mRotateExitAnimation.scaleCurrentDuration(animationScale);
        mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
        mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
        mRotateEnterAnimation.scaleCurrentDuration(animationScale);

        mAnimRunning = false;
        mFinishAnimReady = false;
        mFinishAnimStartTime = -1;

        if (customAnim) {
            mRotateAlphaAnimation.restrictDuration(maxAnimationDuration);
            mRotateAlphaAnimation.scaleCurrentDuration(animationScale);
        }

        if (customAnim && mEnteringBlackFrame == null) {
            try {
                Rect outer = new Rect(-finalWidth, -finalHeight,
                        finalWidth * 2, finalHeight * 2);
                Rect inner = new Rect(0, 0, finalWidth, finalHeight);
                mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
                        SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false, mEnterBlackFrameLayer);
            } catch (OutOfResourcesException e) {
                Slog.w(TAG, "Unable to allocate black surface", e);
            }
        }

        if (customAnim) {
            mSurfaceRotationAnimationController.startCustomAnimation();
        } else {
            mSurfaceRotationAnimationController.startScreenRotationAnimation();
        }

        return true;
    }
  • exitAnim和enterAnim來自前面流程的selectRotationAnimation方法
  • 如果selectRotationAnimation方法沒有獲取到custom動(dòng)畫效果,就加載四種默認(rèn)的效果
  • 加載的Animation賦值給 mRotateEnterAnimation 和 mRotateExitAnimation

后面的startCustomAnimation或startScreenRotationAnimation方法流程將使用mRotateEnterAnimation及mRotateExitAnimation構(gòu)建AnimationSpec炸庞,然后使用SurfaceAnimator啟動(dòng)并驅(qū)動(dòng)動(dòng)畫,還是那個(gè)熟悉的味道钱床。

關(guān)于無縫旋轉(zhuǎn)的具體實(shí)現(xiàn)我后面會(huì)再做深入理解。

至此埠居,WMS動(dòng)畫系統(tǒng)初探完結(jié), 撒花 \ o /

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纸颜,一起剝皮案震驚了整個(gè)濱河市捏浊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浊洞,老刑警劉巖胡岔,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件靶瘸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡怨咪,警方通過查閱死者的電腦和手機(jī)诗眨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巍膘,“玉大人芋簿,你說我怎么就攤上這事》究担” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵裆甩,是天一觀的道長(zhǎng)齐唆。 經(jīng)常有香客問我冻河,道長(zhǎng),這世上最難降的妖魔是什么锭弊? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任味滞,我火速辦了婚禮钮呀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爽醋。我一直安慰自己,他們只是感情好光戈,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布久妆。 她就那樣靜靜地躺著跷睦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奸笤。 梳的紋絲不亂的頭發(fā)上哼鬓,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音健盒,去河邊找鬼。 笑死惰帽,一個(gè)胖子當(dāng)著我的面吹牛父虑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播呜魄,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼莱衩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了睹晒?” 一聲冷哼從身側(cè)響起括细,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤勒极,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后辱匿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡絮短,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年丁频,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了邑贴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奖磁,死狀恐怖繁疤,靈堂內(nèi)的尸體忽然破棺而出秕狰,到底是詐尸還是另有隱情鸣哀,我是刑警寧澤吞彤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站低飒,受9級(jí)特大地震影響懂盐,放射性物質(zhì)發(fā)生泄漏糕档。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一俐银、第九天 我趴在偏房一處隱蔽的房頂上張望端仰。 院中可真熱鬧,春花似錦吱七、人聲如沸鹤竭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吧寺。三九已至,卻和暖如春幕帆,著一層夾襖步出監(jiān)牢的瞬間抒钱,已是汗流浹背颜凯。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工症概, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留早芭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓募壕,卻偏偏與公主長(zhǎng)得像语盈,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子代嗤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容