8.0: Only fullscreen opaque activities can request orientation

背景

AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo
{包名/目標(biāo)路徑Activity}
: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation

項(xiàng)目的線上Firebase監(jiān)控和monkey都指向了 在8.0機(jī)型上啟動(dòng)XXActivity會(huì)crash,無(wú)法啟動(dòng)目標(biāo)Activity,下面會(huì)結(jié)合源碼分析異常拋出原因。

源碼分析

異常拋出位置:源碼路徑,在8.0.0-r4上

源碼路徑.png
ActivityRecord #setRequestedOrientation()

frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java

  void setRequestedOrientation(int requestedOrientation) {
        if (ActivityInfo.isFixedOrientation(requestedOrientation)  //注釋1
                && !fullscreen //注釋2
                && appInfo.targetSdkVersion > O) { //注釋3
            throw new IllegalStateException("Only fullscreen activities can request orientation");
        }

        ...
    }

同時(shí)滿足這三個(gè)條件就會(huì)拋出此異常
  • 注釋2:非全屏 !fullscreen

  • 注釋3:targetSdkVersion的設(shè)置為大于26 appInfo.targetSdkVersion > O

  • 注釋1:Activity是否顯示固定方向,ActivityInfo.isFixedOrientation(requestedOrientation)

ActivityInfo.isFixedOrientation

frameworks/base/core/java/android/content/pm/ActivityInfo.java

    /**
     * Returns true if the activity's orientation is fixed.
     * @hide
     */
    public boolean isFixedOrientation() {
        return isFixedOrientationLandscape() || isFixedOrientationPortrait()
                || screenOrientation == SCREEN_ORIENTATION_LOCKED;
    }

    /**
     * Returns true if the specified orientation is considered fixed.
     * @hide
     */
    static public boolean isFixedOrientation(int orientation) {
        return isFixedOrientationLandscape(orientation) || isFixedOrientationPortrait(orientation);
    }

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

可見只要手動(dòng)固定上面這些位置標(biāo)記,isFixedOrientation都會(huì)返回true , 都會(huì)被檢查

fullscreen的賦值

  1. 賦值是在ActivityRecord的構(gòu)造函數(shù),調(diào)用了靜態(tài)方法 ActivityInfo#isTranslucentOrFloating
image.png
  1. 繼續(xù)看這個(gè)函數(shù) isTranslucentOrFloating()

frameworks/base/core/java/android/content/pm/ActivityInfo.java

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

根據(jù)上面的定義,如果一個(gè)Activity的Style符合下面三個(gè)條件之一,則認(rèn)為不是“fullscreen”:

  • “windowIsTranslucent”為true;

  • “windowIsTranslucent”為false,但“windowSwipeToDismiss”為true;

  • “windowIsFloating“為true;

解決方案

由于谷歌官方已經(jīng)在后續(xù)版本刪除這個(gè)邏輯修復(fù)了,且Android 8占比已經(jīng)很少,那么我們就按照這個(gè)觸發(fā)條件,手動(dòng)去規(guī)避。

  1. 檢查Activity的style是否可以不設(shè)置成透明,以至于不觸發(fā)fullscreen

  2. 移除orientation顯示固定標(biāo)記

 1.在manifest中移除
 android:screenOrientation="portrait"

 2.Activity#oncreate()中動(dòng)態(tài)設(shè)置
 if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
   setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
 }

  1. 全局hook反射,設(shè)置屏幕不固定 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;

http://m.itdecent.cn/p/8328a586f9de

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容