Android 8.0 崩溃,Only fullscreen activities can request orientation

Android 8.0崩溃信息:Only fullscreen activities can request orientationjava

缘由很简单,大概是谷歌爸爸在安卓8.0版本时为了支持全面屏,增长了一个限制:若是是透明的Activity,则不能固定它的方向,由于它的方向实际上是依赖其父Activity的(由于透明)。然而这个bug只有在8.0中有,8.1中已经修复。具体crash有两种:android

1.Activity的风格为透明,在manifest文件中指定了一个方向,则在onCreate中crash程序员

2.Activity的风格为透明,若是调用setRequestedOrientation方法固定方向,则crash先看onCreate中的代码:数组

 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");
            }
        }

 

这个targetVersion有点骚,若是指定android26,还没问题。ide

具体什么是透明,看代码:   ui

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;

    }

大意就是,有上面三种风格就是透明。this

那么什么是固定呢?再看: code

  public boolean isFixedOrientation() {
        return isFixedOrientationLandscape() || isFixedOrientationPortrait()
                || screenOrientation == SCREEN_ORIENTATION_LOCKED;
    }

其实就是横竖屏或者锁定就是固定。ip

如今思路其实很明确,咱们只要修补一个版。若是进onCreate的时候,若是判断是透明窗口风格,直接把屏幕朝向改成未指定类型即SCREEN_ORIENTATION_UNSPECIFIED就能够了,由于Activity是透明的,因此其方向依赖于父Activity,因此这个改动对结果不会产生任何影响。get

至于不少透明Activity的代码中调用setRequestedOrientation,更好处理,项目不是有BaseActivity吗,咱们直接判断若是是androidO版本,咱们不调用它便可,结果也是等效的。

 

网上千篇一概的不是说把Activity改成不透明或者把方向省掉的,还有说不升级targetVersion的,这些方案是在是不太好,由于本人项目中有大量的Theme文件,依赖错综复杂,想理清哪一个Activity是透明的,还真不是件容易的事。

 

首先要作的就是获取当前Activity是否是透明的,上面都有了代码了,只是好多东西都是隐藏的,咱们用反射好了。

安卓O开始,好多类都被谷歌爸爸移动到黑名单,还有好多浅灰、深灰名单的类以及方法,不少调不了,不过没关系,咱们此次只是解决26版本这一个版本,由于咱们须要反射的类都尚未被禁止,因此还能帮谷歌爸爸擦屁股,若是后面谷歌爸爸再犯2出这种bug,想擦都没得擦。

下面这段代码是BaseActivity的成员方法,其中稍难的就是如何获取com.android.internal.R$styleable.Window这个stylable,最开始我R后面写的是“.”,一直不对,后来才发现stylable实际上是R的内部类,获取到这个数组,就能够用反射调用ActivityInfo#isTranslucentOrFloating()这个方法了。   

private boolean isTranslucentOrFloating(){
        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;
    }

在onCreate的时候,先判断,若是透明,直接把方向改成SCREEN_ORIENTATION_UNSPECIFIED: 

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O && isTranslucentOrFloating()) {
            boolean result = fixOrientation();
            XLog.i(XLog.BASE, "onCreate fixOrientation when Oreo, result = " + result);
        }
        super.onCreate(savedInstanceState);
    }

    private boolean fixOrientation(){
        try {
            Field field = Activity.class.getDeclaredField("mActivityInfo");
            field.setAccessible(true);
            ActivityInfo o = (ActivityInfo)field.get(this);
            o.screenOrientation = -1;
            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()) {
            XLog.i(XLog.BASE, "avoid calling setRequestedOrientation when Oreo.");
            return;
        }
        super.setRequestedOrientation(requestedOrientation);
    }

附上程序员交流和福利发放群,平时给程序员发发福利:725030150

相关文章
相关标签/搜索