Android开发经验谈Android开发Android技术知识

360Replugin插件使用中android8.0页面奔溃

2019-04-12  本文已影响8人  大木朵

背景

这两天把一个项目的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");

  1. 当targetSdkVersion >= 27
  2. 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;
    }
  1. 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。
有问题请留言。

上一篇下一篇

猜你喜欢

热点阅读