部分Android 8.0机型的应用闪退问题

Posted by lx8421bcd on March 13, 2019

前言

经过权限适配后,年后准备发版,无聊之中拿朋友的小米测试启动图适配,结果……
居然启动闪退……

报错信息:

java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
......

查了一下,这是源自Google AOSP代码里的bug,看来这又是适配到API26+的一个暗坑,简要记录一下此问题触发的缘由和环境

问题触发环境

某几个版本的Android8.0系统及其衍生ROM

触发条件

应用的targetSdkVerion=26+,Activity在Manifest中声明时,theme引用的style中包含了windowIsTranslucent="true",同时,activity标签本身的属性中包含了screenOrientation="portrait",即会在初始化此Activity时引发崩溃。
比如:

<!-- manifest里的activity声明 -->
<activity 
    android:name=".activity.SplashActivity"
    android:screenOrientation="portrait"
    android:theme="@style/SplashActivityTheme"/>

<!-- style.xml里面的样式声明 -->
<style name="SplashActivityTheme" parent="Theme.AppCompat.Light">
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
</style>

这种样式声明常见于启动页,所以很容易复现。

产生原因

在某几个版本的Android O源码中,有如下逻辑,看起来Google希望通过这种方式告诉开发者不应该修改配置了translucent=true的activity的orientation,只不过这种方法有点过于2B……

//Code in Activity.java
//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");
    }
}

解决方法

目前Google在后续的版本中已经删除了这个2B的判断逻辑,但是部分基于问题源码的ROM仍然在市面上留存,防不胜防,因此此问题还是必须作出兼容适配。
问题的核心在于windowIsTranslucent="true"screenOrientation="portrait"同时存在,所以处理方法是将Manifest声明中的screenOrientation属性删掉,在Activity中根据版本手动指定Orientation。 以上面的SplashActivity声明为例

<!-- manifest里的activity声明 -->
<activity 
    android:name=".activity.SplashActivity"
    android:theme="@style/SplashActivityTheme"/>

Activity的Java代码


// 在onCreate中手动指定orientation
@Override
public void onCreate(Bundle savedInstanceState) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_splash);
    init();
}

相关讨论

java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation