美文网首页
Android O 适配问题 “Only fullscreen

Android O 适配问题 “Only fullscreen

作者: Little丶Jerry | 来源:发表于2020-03-29 20:51 被阅读0次

一、背景:

使用 Android 8.0 手机测试应用自升级功能,安装包下载完成之后应用崩溃了...

震惊之余,赶紧去查看崩溃堆栈。

下面是异常堆栈:

java.lang.RuntimeException: Unable to start activity ComponentInfo:
java.lang.IllegalStateException: Only fullscreen activities can request orientation

Activity 打开异常,这也太心累了。当务之急,还是先查清原因吧。

二、当前项目状况:

1. 项目中 BaseActivity 的 onCreate() 方法通过代码锁定了手机屏幕方向:
public class BaseActivity extends AppCompatActivity {
    @CallSuper
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        //默认设置为竖屏
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
2. 项目的 targetSdkVersion 为 28 > 26:
android = [compileSdkVersion        : 28,
            ...
           targetSdkVersion         : 28,
            ...
"android.support.test.runner.AndroidJUnitRunner"]
3. InstallApkActivity 的主题样式设置了 Window 窗口半透明。
<style name="BaseTranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowBackground">#00000000</item>
    <item name="android:windowIsTranslucent">true</item>//设置窗口半透明
</style>

种种条件叠加起来,导致遇到官方系统做的检查,抛出以下异常:

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

意思是说:只有不透明的全屏 Activity 可以自主设置界面方向。

三、分析:

1. 通过查看 AOSP 的提交:Prevent non-fullscreen activities from influencing orientation,这是 Activity 的源码,可以看到抛出该异常的地方:
@MainThread
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    if (getApplicationInfo().targetSdkVersion > O && mActivityInfo.isFixedOrientation()) {
        final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
        final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
        ta.recycle();

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

即当屏幕方向锁定了并且不为全屏,而且应用的 targetSdkVersion 大于 Android O 的话,就会抛出这个异常。

2. fullscreen 有多个条件控制
Entry ent = AttributeCache.instance().get(packageName,
                realTheme, com.android.internal.R.styleable.Window, userId);
fullscreen = ent != null && !ActivityInfo.isTranslucentOrFloating(ent.array);
3. 展开 isTranslucentOrFloating() 方法
// Determines whether the {@link Activity} is considered translucent or floating.public static boolean isTranslucentOrFloating(TypedArray attributes) {
 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;
}

即只要满足 isFloatingisTranslucentisSwipeToDismiss 条件其中之一都不算 fullscreen,目的是不让非全屏透明的界面决定手机屏幕的朝向,因为透明的界面能透视背景界面。

4. 测试的时候使用 Android 7.1 / 9.0 / 10.0 的手机没有触发这个问题,是因为这代码是在 8.0 版本引入的,而且问题已经在 8.1 版本修复了。具体可以查看这笔提交:DO NOT MERGE Remove orientation restriction to only fullscreen activities.

四、解决方法:

最直接的解决方法就是打破联合条件之一:

  1. 降级 targetSdkVersion 使其不超过 26(不推荐)

  2. 移除 Activity 的 screenOrientation 属性以及移除显式指定屏幕方向的代码

  3. 设置 Activity 主题样式里的 windowIsTranslucent / windowSwipeToDismiss / windowIsFloating 属性为 false

<item name="android:windowIsTranslucent">false</item>

<item name="android:windowSwipeToDismiss">false</item>

<item name="android:windowIsFloating">false</item>
  1. 在 Activity 的 onCreate 方法中使用 try - catch 捕获异常
    protected void onCreate(Bundle savedInstanceState) {
        try {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        } catch (Exception e) {
            Log.e(TAG, e);
        }
        super.onCreate(savedInstanceState);
    }

我们应该根据自己的业务需要来选择和制定方案。

五、总结:

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

但个人看来,官方在处理这个问题上过于粗暴了。正常来说,检查全屏和屏幕方向条件后,应该先警告开发者,且忽略已经指定的设置,保证应用运行时兼容性。结果现在二话不说就抛出异常,有点不太厚道了,我想这也是官方在 8.1 就去掉这段代码的原因。因此如果测试没有覆盖 Android 8.0 版本,或者依赖第三方 SDK 引起问题,可能会导致严重后果。

相关文章

网友评论

      本文标题:Android O 适配问题 “Only fullscreen

      本文链接:https://www.haomeiwen.com/subject/jbtpuhtx.html