背景
这两天把一个项目的targetSdkVersion升级到了28。发现在Android8.0版本的安卓手机上点击桌面图标进入app的话直接奔溃。
关键日志如下:
java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
看了一下,原来启动页面为了消除白屏指定了使用透明主题且指定screenOrientation为竖向,但是Android8.0系统的Activity源码对Activity做了限制,不能同时指定 android:screenOrientation 为 portrait 且 androidwindowIsTranslucent为true,如下
<activity
android:name=".SplashActivity"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
那好把android:screenOrientation="portrait"
去掉就好了,App可顺利进入
问题
主工程的问题解决了,但是插件里设置了透明主题的Activity依然奔溃。
是因为打开的插件内的Activity不是我们自己注册的Activity,其实是Replugin提前注册的坑位Activity,而这些坑位Activity默认指定了android:screenOrientation="portrait"
,我们自己在AndroidManifest.xml
里面设置的不起作用,那怎么办呢?
分析源码
看看Activity奔溃的地方
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
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();
if (isTranslucentOrFloating) {
throw new IllegalStateException(
"Only fullscreen opaque activities can request orientation");
}
}
//....省略
}
满足以下几个条件时会走到throw new IllegalStateException("Only fullscreen opaque activities can request orientation");
- 当targetSdkVersion >= 27
- mActivityInfo.screenOrientation 的值指定为PORTRAIT,LANDSCAPE 等几个值时,相关ActivityInfo类的代码如下
/**
* 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 landscape.
* @hide
*/
boolean isFixedOrientationLandscape() {
return isFixedOrientationLandscape(screenOrientation);
}
/**
* Returns true if the activity's orientation is fixed to landscape.
* @hide
*/
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;
}
/**
* 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;
}
- windowIsTranslucent = true, 或者 windowIsFloating = true, 或者windowSwipeToDismiss = true,相关代码如下
/**
* 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;
}
那直接在onCreate之前把screenOrientation
改成别的不就行了,比如改成SCREEN_ORIENTATION_UNSPECIFIED,开干。
踩坑
1.那我用代码吧,
@Override
protected void onCreate(Bundle savedInstanceState) {
int requestedOrientation = getRequestedOrientation();
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|| requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
super.onCreate(savedInstanceState);
//...
}
失败...原因不详,还是报之前那个错误
解决问题
用反射的方式直接改成员变量里面的mActivityInfo的属性screenOrientation可行,核心代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
RepluginCompatUtils.onCreate(this);
super.onCreate(savedInstanceState);
initUserId(getIntent().getExtras());
}
RepluginCompatUtils.java :
public class RepluginCompatUtils {
/**
* super.onCreate(savedInstanceState) 之前 调用;
*/
public static void onCreate(Activity activity) {
if (activity == null) {
return;
}
int requestedOrientation = activity.getRequestedOrientation();
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|| requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
modifyRequestedOrientation(activity);
}
}
}
private static void modifyRequestedOrientation(Activity activity) {
if (activity == null){
return;
}
try {
Field mActivityInfoField = ReflectUtils.getField(activity, "mActivityInfo");
mActivityInfoField.setAccessible(true);
ActivityInfo activityInfo = (ActivityInfo) mActivityInfoField.get(activity);
activityInfo.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
} catch (Exception e) {
// e.printStackTrace();
}
}
}
GitHub
相关代码可关注 https://github.com/LeeYawei/360RepluginCompatAndroid8.0
欢迎star,follow。
有问题请留言。
网友评论