Android 8.0跳坑之'Only fullscreen opaque activities can request orientation'

一大早测试就蹦出一个坑,楼主是想在点击一张图片形成全屏展示的效果,问题来了只能去解决,
先给出解决方案:

1,了解问题是什么,

‘Only fullscreen opaque activities can request orientation’

翻译:只有全屏不透明的activity可以设置orientation

可以看出问题不出在代码上,而是在样式设计中,

2,先确认自己是不是用的Android8.0版本(楼主只想说只在8.0遇到过这个问题,还是遇到了三次,都不长记性的那种)

3,在AndroidManifast文件中找到相关的Activity类的注册,如果有android:screenOrientation='portrait’啥的麻烦删掉

4,在style文件中找true,然后把true改成false

如果想深入了解,就去看看windowIsTranslucent到底是个什么玩意:

http://blog.csdn.net/kongbaidepao/article/details/52165687

适配到安卓O,适配了Service、通知等等,天真的以为一切都结束了,换菊花厂手机试APP,直接crash,这简直是何等的卧槽。错误提示如下(还有一种和这个差不多,就差一个单词,一个是onCreate时候,另一个是设置方向之后):

Only fullscreen activities can request orientation
先参考了一下网上各位大佬的文章,以下面的为例:

https://zhuanlan.zhihu.com/p/32190223

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

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,还没问题。

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

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

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

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

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

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

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

至于很多透明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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值