Android开发

android10屏幕旋转流程

2019-10-28  本文已影响0人  丸子不爱吃丸子

1.手机怎么检测到屏幕方向发生变化
手机会打开一个OrientationListener回调,当sensor检测到屏幕方向发生变化后会回调到windowManagerService
2.检测到屏幕屏幕方向发生变化后的流程
当确认屏幕发生旋转则调用旋转流程先截屏然后旋转然后relayout。

屏幕方向发生旋转是根据sensor回调来通知系统。
整体流程如下图,调用链还是比较复杂的

rotation.jpg

当从systemUI或者Settings中打开自动转屏开关后,DisplayRotation的内部类SettignsObserver会监听到,然后会打开sensor监听屏幕旋转。
看一下调用栈:
DisplayRotation.SettingsObserver.onChange()->updateSettings()->updateOritationListenerLw()->OritaionListener.enable()

这个一个典型的观察者模式,有兴趣同学可以了解一下观察者模式。
直接看OritaionListener代码

    private class OrientationListener extends WindowOrientationListener {
        final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
        boolean mEnabled;

        OrientationListener(Context context, Handler handler) {
            super(context, handler);
        }

        private class UpdateRunnable implements Runnable {
            final int mRotation;

            UpdateRunnable(int rotation) {
                mRotation = rotation;
            }

            @Override
            public void run() {
                // Send interaction hint to improve redraw performance.
                mService.mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
                if (isRotationChoicePossible(mCurrentAppOrientation)) {
                    final boolean isValid = isValidRotationChoice(mRotation);
                    sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
                } else {
//这里发生屏幕旋转通知windowManagerService
                    mService.updateRotation(false /* alwaysSendConfiguration */,
                            false /* forceRelayout */);
                }
            }
        }

        @Override
        public void onProposedRotationChanged(int rotation) {
            if (DEBUG_ORIENTATION) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);
            Runnable r = mRunnableCache.get(rotation, null);
            if (r == null) {
                r = new UpdateRunnable(rotation);
                mRunnableCache.put(rotation, r);
            }
            getHandler().post(r);
        }

//打开sensor监听
        @Override
        public void enable(boolean clearCurrentRotation) {
            super.enable(clearCurrentRotation);
            mEnabled = true;
            if (DEBUG_ORIENTATION) Slog.v(TAG, "Enabling listeners");
        }
//关闭sensor监听
        @Override
        public void disable() {
            super.disable();
            mEnabled = false;
            if (DEBUG_ORIENTATION) Slog.v(TAG, "Disabling listeners");
        }
    }

如果enable listener当发生屏幕旋转事件后会回调到DisplayRoation,然后回到WindowManagerService准备旋转屏幕

mService.updateRotation(false /* alwaysSendConfiguration */,
                            false /* forceRelayout */);

windowManagerService中的updateRotaion这里开始屏幕旋转流程。

看一下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) {
        ...  
        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);
                   //调用displayContent 继续判断是否需要旋转屏幕
                    final boolean rotationChanged = displayContent.updateRotationUnchecked();
 
                    if (!rotationChanged || forceRelayout) {
                        displayContent.setLayoutNeeded();
                        layoutNeeded = true;
                    }
                    if (rotationChanged || alwaysSendConfiguration) {
                        displayContent.sendNewConfiguration();
                    }
                }

                if (layoutNeeded) {
                    mWindowPlacerLocked.performSurfacePlacement();
                }
            }
        } finally {
      ...
        }
    }

DisplayContent:

    boolean updateRotationUnchecked(boolean forceUpdate) {
     
       .............

 //这里省略了很多代码,其实这里有很重要的一些步骤,都是在判断当前屏幕是否需要旋转,
//由于我们这次讨论的重点在于屏幕旋转的流程,就不在细看其他流程。
//关于android如何判断屏幕发生了旋转,后面会写一篇博客来说明
//开始冻结屏幕
           mWmService.startFreezingDisplayLocked(anim[0], anim[1], this);
            // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
        return true;
    }

这里如果检测需要旋转屏幕,则先冻结屏幕,然后拿到冻结的这一帧做一个旋转动画

WindowManagerService:

   //冻结屏幕
    void startFreezingDisplayLocked(int exitAnim, int enterAnim,
            DisplayContent displayContent) {

//这里创建screenRotationAnimation
            screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
                    displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
                    this);
            mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                    screenRotationAnimation);
        }
    }

创建转屏动画的时候对当前屏幕截屏,拿到surfaceFlinger一帧

    public ScreenRotationAnimation(Context context, DisplayContent displayContent,
            boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {

          //拿到一帧
            SurfaceControl.ScreenshotGraphicBuffer gb =
                mService.mDisplayManagerInternal.screenshot(displayId);
            ..............
   }

接下来就是对这一帧做旋转动画
然后看一下旋转动画,在WindowManagerService中

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

这里会发消息给handler,收到消息后会调用到PerformSurfacePlacement

    public WindowSurfacePlacer(WindowManagerService service) {
        mService = service;
        mPerformSurfacePlacement = () -> {
            synchronized (mService.mGlobalLock) {
                performSurfacePlacement();
            }
        };
    }

    final void performSurfacePlacement(boolean force) {
       int loopCount = 6;
        do {
            mTraversalScheduled = false;
            performSurfacePlacementLoop();
            ...........

经过一系列调用,最后来到stopFreezingScreen

    public void stopFreezingScreen() {
        synchronized (mGlobalLock) {
            if (mClientFreezingScreen) {
                try {
                 //停止屏幕冻结
                    stopFreezingDisplayLocked();
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }
        }
    }

    void stopFreezingDisplayLocked() {
        if (!mDisplayFrozen) {
            return;
        }
           //调用dismiss
            if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
                    getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
                        displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {

    ..........
    }

dismiss流程

    public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
            float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
     //开始屏幕旋转动画
        if (!mStarted) {
            startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
                    true, exitAnim, enterAnim);
        }

    }

开始转屏动画

    private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
            float animationScale, int finalWidth, int finalHeight, boolean dismissing,
            int exitAnim, int enterAnim) {
        if (mSurfaceControl == null) {
            // Can't do animation.
            return false;
        }
        if (mStarted) {
            return true;
        }

        mStarted = true;

        boolean firstStart = false;

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

        if (TWO_PHASE_ANIMATION && mFinishExitAnimation == null
                && (!dismissing || delta != Surface.ROTATION_0)) {
            if (DEBUG_STATE) Slog.v(TAG, "Creating start and finish animations");
            firstStart = true;
            mStartExitAnimation = AnimationUtils.loadAnimation(mContext,
                    com.android.internal.R.anim.screen_rotate_start_exit);
}

最后发送 config消息,通知activity ReLaunch

        at com.android.server.wm.ActivityRecord.relaunchActivityLocked(ActivityRecord.java:3584)
        at com.android.server.wm.ActivityRecord.ensureActivityConfiguration(ActivityRecord.java:3462)
        at com.android.server.wm.ActivityRecord.ensureActivityConfiguration(ActivityRecord.java:3280)
        at com.android.server.wm.ActivityTaskManagerService.ensureConfigAndVisibilityAfterUpdate(ActivityTaskManagerService.java:5971)
        at com.android.server.wm.ActivityTaskManagerService.updateDisplayOverrideConfigurationLocked(ActivityTaskManagerService.java:5494)
        at com.android.server.wm.ActivityTaskManagerService.updateDisplayOverrideConfiguration(ActivityTaskManagerService.java:4599)
        at com.android.server.wm.WindowManagerService.sendNewConfiguration(WindowManagerService.java:4632)

总结一下:
1.sensor检测到屏幕方向发生变化,回调到wms
2.wms判断当前需要旋转屏幕,则截屏,dismiss,旋转
3.屏幕旋转完成,relayout window ,relaunch activity

上一篇下一篇

猜你喜欢

热点阅读