问题
最近同事把 targetSdkVersion 从 26 升到 28 后,原本以为没有兼容性问题,没真机检查就发测试版。结果内部试用 Android 8.0 和 Android 8.1 手机打开 Activity 崩溃。
上报错误具体如下:
1
java.lang.RuntimeException:Unable to start activity ComponentInfo{com.xx.xx/com.xx.xxmessage.chat.ui.RoomActivity}: java.lang.IllegalStateException: Only fullscreen activities can request orientation
其他开发者也遇到相同问题:
- Only fullscreen opaque activities can request orientation
- 解决Android 8.0的Only fullscreen opaque activities can request orientation
- Android 8.0跳坑之’Only fullscreen opaque activities can request orientation’
状况
一直都在 Activity 基类的 onCreate() 中用代码锁定屏幕方向
1
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
除了用代码指定会出现问题,在 AndroidMenifest 指定该参数也不能幸免:
1
android:screenOrientation="portrait"
这个问题并不仅因单个条件引起,因为界面要做右滑退出,所以Window背景必须设置为透明
1
<item name="android:windowIsTranslucent">true</item>
结果就碰到官方系统做的检查,出现标题的异常
排查
读AOSP的提交:Prevent non-fullscreen activities from influencing orientation,抽出代码如下:
1
2
3
4
5
if (ActivityInfo.isFixedOrientation(requestedOrientation)
&& !fullscreen
&& appInfo.targetSdkVersion >= O) {
throw new IllegalStateException("Only fullscreen activities can request orientation");
}
fullscreen 有多个条件控制
1
2
3
4
Entry ent = AttributeCache.instance().get(packageName,
realTheme, com.android.internal.R.styleable.Window, userId);
fullscreen = ent != null && !ActivityInfo.isTranslucentOrFloating(ent.array);
展开 isTranslucentOrFloating()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Determines whether the {@link Activity} is considered translucent or floating.
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;
}
只要满足 isFloating、isTranslucent、!windowIsTranslucent && isSwipeToDismiss 条件其中之一都不算 fullscreen,目的是不让 非全屏 或 透明 的界面决定手机界面的朝向,因为透明的界面能透视背景界面。
解决方案
解决这个问题就是打破联合条件之一:
- 可移除已经制定的透明或半透明属性;
- 或移除显式指定屏幕方向的代码;
- 或 targetSdkVersion 不超过 26 ;
看法
从个人角度来看,官方在这个问题上的处理手段极为粗暴。正常来说,检查全屏和屏幕方向条件后, 应该先警告开发者,且忽略已经指定的设置,保证应用运行时兼容性。
结果现在非要粗暴抛出异常,非常不厚道。如果测试没有覆盖 Android 8.0 - Android 8.1,只验证 Android9.0,或者依赖第三方SDK引起问题,导致后果非常严重