Only fullscreen activities can request orientation优雅解决方案
—— 星光不问赶路人,时光不负有心人。
问题
当我们把targetSdkVersion升级到26及以上,buildToolsVersion和相关的support library升级到26.0.1及以上,在Android 8.0(API level 26)上,部分Activity出现了一个莫名其妙的crash,异常信息如下:
java.lang.RuntimeException: Unable to start activity ComponentInfo: java.lang.IllegalStateException: Only fullscreen activities can request orientation
问题原因
这个问题貌似已经被广泛的讨论了,最终我们锁定了April 26的一个commit:
这个改动中抛出异常有关的代码如下:
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在26及以上,当前Activity是isFixedOrientation的并且是isTranslucentOrFloating的则抛出异常。下面,我们看一下isFixedOrientation如何定义的:
public boolean isFixedOrientation() { return isFixedOrientationLandscape() || isFixedOrientationPortrait() || screenOrientation == SCREEN_ORIENTATION_LOCKED; } public static boolean isFixedOrientationLandscape(@ScreenOrientation int orientation) { return orientation == SCREEN_ORIENTATION_LANDSCAPE || orientation == SCREEN_ORIENTATION_SENSOR_LANDSCAPE || orientation == SCREEN_ORIENTATION_REVERSE_LANDSCAPE || orientation == SCREEN_ORIENTATION_USER_LANDSCAPE; } 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; }
很明显,上面的意思是如果当前Activity的屏幕方向是锁定的或固定横竖屏的,那么这个Activity就是isFixedOrientation的。继续看isTranslucentOrFloating:
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; }
根据上面的定义,如果Activity符合下面三个条件之一,那么就是isTranslucentOrFloating的:
1. “windowIsTranslucent”为true;
2. “windowIsTranslucent”为false,但“windowSwipeToDismiss”为true;
3. “windowIsFloating“为true;
问题修复
现在思路其实很明确,从上面可以看出,只要三个条件同时成立,才会抛出Only fullscreen opaque activities can request orientation异常。首先我们不可能去修改项目的targetSdkVersion配置,这个配置是我们项目中做好兼容与适配的SDK版本。其次我们也不能去修改每个Activity的windowIsTranslucent、windowSwipeToDismiss、windowIsFloating的属性,因为这会影响到每个Activity的效果。所以我们只有从isFixedOrientation着手了。我们来看,首先这个bug只有在Android8.0版本上才会出现, 所以我们只要针对Android8.0进行处理即可,即如果当前设备是Android8.0并且当前Activity是isTranslucentOrFloating的,那么就把屏幕方向改为未指定类型即SCREEN_ORIENTATION_UNSPECIFIED,这样isFixedOrientationPortrait条件就不会成立了。
@Override protected void onCreate(Bundle savedInstanceState) { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O && isTranslucentOrFloating()) { fixOrientation(); } } private boolean isTranslucentOrFloating() { ActivityInfo 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); m.setAccessible(false); } catch (Exception e) { e.printStackTrace(); } return isTranslucentOrFloating; } private boolean fixOrientation() { try { Field field = Activity.class.getDeclaredField("mActivityInfo"); field.setAccessible(true); ActivityInfo o = (ActivityInfo) field.get(this); o.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; field.setAccessible(false); return true; } catch (Exception e) { e.printStackTrace(); } return false; } @Override public void setRequestedOrientation(int requestedOrientation) { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O && isTranslucentOrFloating()) return; super.setRequestedOrientation(requestedOrientation); }
------转载请注明出处,感谢您对原创作者的支持------
有偿提供技术支持、Bug修复、项目外包、毕业设计、大小作业
Android学习小站
Q Q:1095817610
微信:jx-helu
邮箱:1095817610@qq.com
添加请备注"Android学习小站"
