Android开发Android开发经验谈

8.0: Only fullscreen opaque acti

2022-05-11  本文已影响0人  smart_dev

背景

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

        ...
    }

同时满足这三个条件就会抛出此异常

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”:

解决方案

由于谷歌官方已经在后续版本删除这个逻辑修复了,且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

上一篇下一篇

猜你喜欢

热点阅读