Only fullscreen opaque activities can request orientation

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。
未经允许不得转载

目录

  • 一、导读
  • 二、概览
  • 三、分析
  • 四、 推荐阅读

一、导读

我们继续总结学习基础知识,温故知新。

本文记录一次bug解决的过程,

Only fullscreen opaque activities can request orientation

二、概览

今天将 targetSdkVersion 的版升级到了29,出现了一些奇怪的报错,日志如下


# main(1)

java.lang.IllegalStateException

Only fullscreen opaque activities can request orientation

解析原始
java.lang.RuntimeException:Unable to start activity ComponentInfo{com.xxx/com.xyz.QActivity}: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3303)
......
Caused by:
java.lang.IllegalStateException:Only fullscreen opaque activities can request orientation
android.app.Activity.onCreate(Activity.java:10811)
androidx.core.app.ComponentActivity.void onCreate(android.os.Bundle)(ComponentActivity.java:861)
androidx.activity.ComponentActivity.void onCreate(android.os.Bundle)(ComponentActivity.java:3231)
androidx.fragment.app.FragmentActivity.void onCreate(android.os.Bundle)(FragmentActivity.java:2731)
com.xyz.QActivity.void onCreate(android.os.Bundle)(QActivity.java:518)
android.app.Activity.performCreate(Activity.java:7383)
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:11218)
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:31256)
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:31411)
android.app.ActivityThread.-wrap12(Unknown Source:0)
android.app.ActivityThread$H.handleMessage(ActivityThread.java:19194)
android.os.Handler.dispatchMessage(Handler.java:1018)
android.os.Looper.loop(Looper.java:166)
android.app.ActivityThread.main(ActivityThread.java:75129)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:2145)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:9211)

这个问题只有8。0的系统才会出现,下面我们一起来分析下原因。

三、分析

报的错误是只有全屏不透明的activity可以设置屏幕方向,那我们先看看activit的设置

    <activity
        android:name="com.xyz.QActivity"
        android:launchMode="singleTop"
        android:screenOrientation="portrait"           设置方向
        android:theme="@style/TransparentTheme" />     透明主题

以上我们设置了两个属性,我们再来看看源码,为什么8.0会报错

protected void onCreate(@Nullable Bundle savedInstanceState) {
 
    // 在 onCreate() 方法中会进行屏幕配置检查
    // 如果固定屏幕方向,且设置了透明色或者悬浮,则会抛出异常
    if (getApplicationInfo().targetSdkVersion > O && mActivityInfo.isFixedOrientation()) {
         if (isTranslucentOrFloating) {
             throw new IllegalStateException(
                        "Only fullscreen opaque activities can request orientation");
         }
    }
    //....
} 
 
 
//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;
}

知道了原因,我们来看看怎么解决问题,其思路是这样的,

  1. 检查是否透明色或者悬浮
  2. 如果有的话就设置屏幕不固定

说大白话就是进onCreate的时候,判断是透明窗口风格,把屏幕朝向改为未指定类型SCREEN_ORIENTATION_UNSPECIFIED,
因为Activity是透明的,所以其方向依赖于父Activity,所以这个改动对结果不会产生任何影响。

那我们怎么判断是否透明呢?答案是反射ActivityInfo,获取isTranslucentOrFloating的返回值,


    private boolean isTranslucentOrFloating() {
        boolean isTranslucentOrFloating = false;
        try {
            int[] styleableRes = (int[]) Class.forName("com.android.internal.R$styleable").getField("Window").get(null);
            final TypedArray ta = obtainStyledAttributes(styleableRes);
            Method m = ActivityInfo.class.getMethod("isTranslucentOrFloating", TypedArray.class);
            m.setAccessible(true);
            isTranslucentOrFloating = (boolean) m.invoke(null, ta);
            ta.recycle();
            m.setAccessible(false);
        } catch (Exception e) {
        }
        return isTranslucentOrFloating;
    }

那怎么设置方向呢?,答案还是反射


/**
 *  反射
 **/
    private static void fixOrientation(Activity activity) {
        
        try {
		
            Class activityClass = Activity.class;
            Field mActivityInfoField = activityClass.getDeclaredField("mActivityInfo");
            mActivityInfoField.setAccessible(true);
            
            ActivityInfo activityInfo = (ActivityInfo) mActivityInfoField.get(activity);
            
			// 获取activity 方向是否是固定的
            Method isFixedOrientation = ActivityInfo.class.getMethod("isFixedOrientation");
            isFixedOrientation.setAccessible(true);
            
			// 获取方法的值,即 源码中隐藏的方法 isFixedOrientation()
            boolean hasFixedOri = (boolean) isFixedOrientation.invoke(activityInfo);
            
            isFixedOrientation.setAccessible(false);
			
			// 如果是固定方向
            if (hasFixedOri) { // 当然我们也可以把判断过程去掉
                //设置屏幕不固定
                activityInfo.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_BEHIND;
            } else{
			    // do nothing
			}
			
            mActivityInfoField.setAccessible(false);
        } catch (Exception e) {
        }
		
		
        try {
            Class 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) {
        }  
    }
	
	
	下面这个是源码
    /**
     * Returns true if the activity's orientation is fixed.
     * @hide
     */
    public boolean isFixedOrientation() {
        return isFixedOrientationLandscape() || isFixedOrientationPortrait()
                || screenOrientation == SCREEN_ORIENTATION_LOCKED;
    }

当然,我们还需要进行一次版本的判断


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    //切记:在父类oncreate()方法调用前调用该方法修改配置
    if (Build.VERSION.SDK_INT == 26 && isTranslucentOrFloating()) {
        fixOrientation(this);
    }
    super.onCreate(savedInstanceState);
}

@Override
protected void setRequestedOrientation(int requestedOrientation) {
    // 这里也最好改下,最好是不要让主动进行设置
    if (Build.VERSION.SDK_INT == 26 && isTranslucentOrFloating()) {
        return;
    }
}

另外, Orientation 新增了已个behind属性,表示当前的Activity和栈中在它下方的Activity使用相同的方向。建议这里也改一下。

    <activity
        android:name="com.xyz.QActivity"
        android:launchMode="singleTop"
        android:screenOrientation="behind"           设置方向
        android:theme="@style/TransparentTheme" />     透明主题

四、 推荐阅读

Java 专栏

SQL 专栏

数据结构与算法

Android学习专栏

未经允许不得转载

ddd

版权声明:本文为博主作者:Android西红柿原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/fumeidonga/article/details/136580698

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2024年4月10日
下一篇 2024年4月10日

相关推荐