8.0: Only fullscreen opaque acti

作者: smart_dev | 来源:发表于2022-05-11 01:10 被阅读0次

    背景

    AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo
    {包名/目标路径Activity}
    : java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
    
    

    项目的线上Firebase监控和monkey都指向了 在8.0机型上启动XXActivity会crash,无法启动目标Activity,下面会结合源码分析异常抛出原因。

    源码分析

    异常抛出位置:源码路径,在8.0.0-r4上

    源码路径.png
    ActivityRecord #setRequestedOrientation()

    frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java

      void setRequestedOrientation(int requestedOrientation) {
            if (ActivityInfo.isFixedOrientation(requestedOrientation)  //注释1
                    && !fullscreen //注释2
                    && appInfo.targetSdkVersion > O) { //注释3
                throw new IllegalStateException("Only fullscreen activities can request orientation");
            }
    
            ...
        }
    
    
    同时满足这三个条件就会抛出此异常
    • 注释2:非全屏 !fullscreen

    • 注释3:targetSdkVersion的设置为大于26 appInfo.targetSdkVersion > O

    • 注释1:Activity是否显示固定方向,ActivityInfo.isFixedOrientation(requestedOrientation)

    ActivityInfo.isFixedOrientation

    frameworks/base/core/java/android/content/pm/ActivityInfo.java

        /**
         * Returns true if the activity's orientation is fixed.
         * @hide
         */
        public boolean isFixedOrientation() {
            return isFixedOrientationLandscape() || isFixedOrientationPortrait()
                    || screenOrientation == SCREEN_ORIENTATION_LOCKED;
        }
    
        /**
         * Returns true if the specified orientation is considered fixed.
         * @hide
         */
        static public boolean isFixedOrientation(int orientation) {
            return isFixedOrientationLandscape(orientation) || isFixedOrientationPortrait(orientation);
        }
    
        /**
         * Returns true if the activity's orientation is fixed to landscape.
         * @hide
         */
        boolean isFixedOrientationLandscape() {
            return isFixedOrientationLandscape(screenOrientation);
        }
    
        /**
         * Returns true if the activity's orientation is fixed to landscape.
         * @hide
         */
        public static boolean isFixedOrientationLandscape(@ScreenOrientation int orientation) {
            return orientation == SCREEN_ORIENTATION_LANDSCAPE
                    || orientation == SCREEN_ORIENTATION_SENSOR_LANDSCAPE
                    || orientation == SCREEN_ORIENTATION_REVERSE_LANDSCAPE
                    || orientation == SCREEN_ORIENTATION_USER_LANDSCAPE;
        }
    
            /**
         * Returns true if the activity's orientation is fixed to portrait.
         * @hide
         */
        boolean isFixedOrientationPortrait() {
            return isFixedOrientationPortrait(screenOrientation);
        }
    
        /**
         * Returns true if the activity's orientation is fixed to portrait.
         * @hide
         */
        public static boolean isFixedOrientationPortrait(@ScreenOrientation int orientation) {
            return orientation == SCREEN_ORIENTATION_PORTRAIT
                    || orientation == SCREEN_ORIENTATION_SENSOR_PORTRAIT
                    || orientation == SCREEN_ORIENTATION_REVERSE_PORTRAIT
                    || orientation == SCREEN_ORIENTATION_USER_PORTRAIT;
        }
    
    

    可见只要手动固定上面这些位置标记,isFixedOrientation都会返回true , 都会被检查

    fullscreen的赋值

    1. 赋值是在ActivityRecord的构造函数,调用了静态方法 ActivityInfo#isTranslucentOrFloating
    image.png
    1. 继续看这个函数 isTranslucentOrFloating()

    frameworks/base/core/java/android/content/pm/ActivityInfo.java

        /**
         * Determines whether the {@link Activity} is considered translucent or floating.
         * @hide
         */
        public static boolean isTranslucentOrFloating(TypedArray attributes) {
            final boolean isTranslucent =
                    attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent,
                            false);
            final boolean isSwipeToDismiss = !attributes.hasValue(
                    com.android.internal.R.styleable.Window_windowIsTranslucent)
                    && attributes.getBoolean(
                            com.android.internal.R.styleable.Window_windowSwipeToDismiss, false);
            final boolean isFloating =
                    attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating,
                            false);
    
            return isFloating || isTranslucent || isSwipeToDismiss;
        }
    
    

    根据上面的定义,如果一个Activity的Style符合下面三个条件之一,则认为不是“fullscreen”:

    • “windowIsTranslucent”为true;

    • “windowIsTranslucent”为false,但“windowSwipeToDismiss”为true;

    • “windowIsFloating“为true;

    解决方案

    由于谷歌官方已经在后续版本删除这个逻辑修复了,且Android 8占比已经很少,那么我们就按照这个触发条件,手动去规避。

    1. 检查Activity的style是否可以不设置成透明,以至于不触发fullscreen

    2. 移除orientation显示固定标记

     1.在manifest中移除
     android:screenOrientation="portrait"
    
     2.Activity#oncreate()中动态设置
     if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
       setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
     }
    
    
    1. 全局hook反射,设置屏幕不固定 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;

    https://www.jianshu.com/p/8328a586f9de

    相关文章

      网友评论

        本文标题:8.0: Only fullscreen opaque acti

        本文链接:https://www.haomeiwen.com/subject/qearurtx.html