Android8.0适配-Only fullscreen opaque activities can request orientation

9,854 阅读2分钟

背景

2018年7月18日上午,电信终端产业协会(TAF)发布《移动应用软件高API等级预置与分发自律公约》(以下简称《公约》)。OPPO、华为、百度、360、阿里、小米、VIVO、腾讯作为发起单位,共同签署《公约》并发出联合倡议:号召广大移动应用软件预置与分发服务提供者,拒绝上架并及时更新低API等级的应用,共同维护用户权益。《公约》规定,自2019年5月1日起,新上架和预置应用应基于Android 8.0 (API等级26)及以上开发。自2019年8月1日起,现有应用的更新应基于Android 8.0 (API等级26)及以上开发。

老项目开始做高版本的适配,这不将targetSDK版本改为最新的28,问题不断,这里特地做个记录

异常日志

Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
                                                     at android.app.Activity.onCreate(Activity.java:1081)
                                                     at android.support.v4.app.BaseFragmentActivityDonut.onCreate(BaseFragmentActivityDonut.java:39)
                                                     at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:278)
                                                     at com.seeyon.mobile.android.model.gesture.activity.BaseGuestureActivity.onCreate(BaseGuestureActivity.java:31)
                                                     at com.seeyon.mobile.android.model.base.BaseActivity.onCreate(BaseActivity.java:83)
                                                     at com.seeyon.mobile.android.model.base.ActionBarBaseActivity.onCreate(ActionBarBaseActivity.java:61)
                                                     at com.seeyon.mobile.android.model.flow.FlowSearchActivity.onCreate(FlowSearchActivity.java:53)
                                                     at android.app.Activity.performCreate(Activity.java:7372)
                                                     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1218)
                                                     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3147)
                                                     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3302) 
                                                     at android.app.ActivityThread.-wrap12(Unknown Source:0) 
                                                     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1891) 
                                                     at android.os.Handler.dispatchMessage(Handler.java:108) 
                                                     at android.os.Looper.loop(Looper.java:166) 
                                                     at android.app.ActivityThread.main(ActivityThread.java:7425) 
                                                     at java.lang.reflect.Method.invoke(Native Method) 

首先这是google的一个bug,别等修复了,现在9.0了还是有问题。

源码

//Need to pay attention mActivityInfo.isFixedOrientation() and ActivityInfo.isTranslucentOrFloating(ta)
    if (getApplicationInfo().targetSdkVersion >= O_MR1 && mActivityInfo.isFixedOrientation()) {
        final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
        final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
        ta.recycle();

        //Exception occurred
        if (isTranslucentOrFloating) {
            throw new IllegalStateException(
                    "Only fullscreen opaque activities can request orientation");
        }
    }

从上述代码,不难看出报错的原因,就是 isTranslucentOrFloating 的activity不能是固定orientation的

        * Returns true if the activity's orientation is fixed.
        * @hide
        */
        public boolean isFixedOrientation() {
            return isFixedOrientationLandscape() || isFixedOrientationPortrait()
                    || screenOrientation == SCREEN_ORIENTATION_LOCKED;
        }
        /**
        * 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;
        }

        /**
        * 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;
        }

从上面的源代码中可以知道,解决问题的方法

解决方法

  • 不固定方向

比如去除 android:screenOrientation="portrait",或者不调用设置方向的代码

  • isTranslucentOrFloating 返回false

    只有android:windowIsTranslucent

<item name="android:windowIsTranslucent">false</item>
若同时还有windowIsFloating
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowIsFloating">false</item>

总结:

这个改动的目的是想阻止非全屏的Activity锁定屏幕旋转,因为当前Activity是透明的,浮动的或可滑动取消的,是否锁屏应该由全屏的Activity决定,而不是并没有全部占据屏幕的Activity决定。