Android8.X异常之Only fullscreen opaque activities can request orientation

前言

这几天在Sentry上发现了1个从未见过的异常
java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
印象中从未遇到过这个问题,但也一时觉得莫名其妙,经过查证是只有Android8.X版本才会出现的bug,而且估计很少有人遇到,既然自己解决了也就把怎么解决的说一下。

bug.png

产生原因

简单来说满足3个条件时会报这个异常
1)当前Activity是否固定了方向,例如android:screenOrientation="portrait"
2)当前Activity的主题是否设置了如下的属性:
windowIsTranslucent为true
windowIsTranslucent为false,但windowSwipeToDismiss为true
windowIsFloating为true
3)项目的targetSdkVersion是否为26或27即Android8.X版本
当满足以上条件你又去打开那个Activity,这个bug就会如约而至

解决方案

这里就直接上代码吧

/**
 * 修复Android8.X存在的java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation问题
 * 在Activity中onCreate()中super之前调用
 *
 * @author yzt 2020/6/30
 */
public class ActivityOrientationHook {

    public static void hook(Activity activity) {
        if (activity == null)
            return;

        //目标版本8.X
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1
                && Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
            if (isTranslucentOrFloating(activity)) {
                fixOrientation(activity);
            }
        }
    }

    /**
     * 检查屏幕是否固定,横竖屏或者锁定就是固定的
     */
    private static boolean isTranslucentOrFloating(Activity activity) {
        boolean isTranslucentOrFloating = false;
        try {
            Class<?> styleableClass = Class.forName("com.android.internal.R$styleable");
            Field WindowField = styleableClass.getDeclaredField("Window");
            WindowField.setAccessible(true);
            int[] styleableRes = (int[]) WindowField.get(null);
            //先获取到TypedArray
            final TypedArray typedArray = activity.obtainStyledAttributes(styleableRes);
            Class<?> ActivityInfoClass = ActivityInfo.class;
            //调用检查是否屏幕旋转
            Method isTranslucentOrFloatingMethod = ActivityInfoClass.getDeclaredMethod("isTranslucentOrFloating", TypedArray.class);
            isTranslucentOrFloatingMethod.setAccessible(true);
            isTranslucentOrFloating = (boolean) isTranslucentOrFloatingMethod.invoke(null, typedArray);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return isTranslucentOrFloating;
    }

    /**
     * 设置屏幕不固定,绕过检查
     */
    private static void fixOrientation(Activity activity) {
        try {
            Class<Activity> activityClass = Activity.class;
            Field mActivityInfoField = activityClass.getDeclaredField("mActivityInfo");
            mActivityInfoField.setAccessible(true);
            ActivityInfo activityInfo = (ActivityInfo) mActivityInfoField.get(activity);
            //设置屏幕不固定
            activityInfo.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

然后在对应的Activity中这么使用即可

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ActivityOrientationHook.hook(this);
        super.onCreate(savedInstanceState);
    }

解决思路就是判断targetSdkVersion版本,如果为8.X版本则不固定方向,来达到规避这个异常的目的。由于这个问题在9.0及以上的版本已经解决,所以这里只要判断一下是否是8.X版本即可。

在此感谢

https://blog.csdn.net/na2609613672/article/details/89403053
https://www.jianshu.com/p/8328a586f9de

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容