Help us understand the problem. What is going on with this article?

java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation

More than 1 year has passed since last update.

最近遭遇したクラッシュについて、久しぶりにQiita記事を書いてみようと思います :hugging:

クラッシュについて

端的に書くと、

  • Android 8.0の端末である
  • targetSdkVersionを27以上にしている
  • 背景を透過にしている
  • 画面の向きを固定している

という4つの条件を満たすと、以下のようなクラッシュが発生します。

java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation

Android 8.1以降では発生しないようです。すなわち、targetSdkVersionを27にしたら、Android 8.0(=APIレベル26)のみでクラッシュするということです。

バージョン APIレベル
Android 8.0 26
Android 8.1 27

コードで書くと以下のような感じです。

targetSdkVersionを27以上にしている

app/build.gradle
android {
    defaultConfig {
        targetSdkVersion 27
    }
}

背景を透過にしている

styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="Translucent" parent="AppTheme">
        <item name="android:windowIsTranslucent">true</item>
    </style>
</resources>

画面の向きを固定している

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest>
    <application>
        <activity
            android:name=".TranslucentActivity"
            android:configChanges="orientation"
            android:screenOrientation="portrait"
            android:theme="@style/Translucent" />
    </application>
</manifest>

上記のTranslucentActivityをAndroid 8.0の端末で開くとクラッシュします。

原因

Android 8.0のActivity#onCreateには、以下のような処理が入っているため、上記条件を満たすとクラッシュするようになっています。

Activity.java
public class Activity {
    protected void onCreate(@Nullable Bundle 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");
            }
        }

        // ...
    }
}

しかし、Android 8.1では上述の処理が消されています。

Activity.javacommit logを見てみると、だいたい以下のような時系列で修正が入っていそうです。

commit / tag 説明
android-7.1.2_r36 Android 7.1.2
3979159
d1ac18c
該当箇所の追加
android-8.0.0_r1
android-8.0.0_r36
Android 8.0
a89b183
e83f34c
d4ecffa
該当箇所の削除
android-8.1.0_r1 Android 8.1
a4ceea0 (同様に削除)

APIレベルとタグの関係は以下のページを参考にしました
https://source.android.com/setup/start/build-numbers

バグなのか?仕様なのか?

完全に予想ですが、うしろのActivityが見えているようなActivityの場合、orientationを指定するとうしろのActivityも同じorientationにならなければならず、その時にうしろのActivityはorientationを固定していても画面回転が起こってしまうというのを防ぎたかったのかなと思います。
とはいえ、Android 8.1では削除されている事実からすると、少なくともクラッシュするという挙動はAndroid開発者たちには受け入れられなかったようです。以下で話し合われているのはActivity#onCreateの部分ではないですが、そのようなやりとりがあります。
https://issuetracker.google.com/issues/68454482

解決方法

当たり前ですが、クラッシュしてしまう4つの条件のうち、いずれかを満たさないようにすれば解決できます。とはいえ、Android 8.0を使わせないとか、一生targetSdkVersionを27以上にしないとかはできないと思うので、以下のような解決策になると思います。

画面回転に対応する

本来は画面回転に対応するのがベストだと思います!とはいえこの問題に遭遇する人は画面回転に対応していないアプリを開発している気がします :sweat_smile:

orientationをbehindする

遷移元のActivityが自分のアプリに限定されるのであれば、遷移元のActivityの向きを固定することができます。そうした場合、orientationをbehindにすると、うしろに透けているActivityと同じ向きになるため、そのまま固定されます。
(透明なActivityの場合、orientation未指定でもbehind指定時と同じ挙動になるようですが、behindを指定しておくと明示できて良い気がします)

targetSdkVersionを一瞬下げる

完全に力技です :triumph: :triumph: :triumph:
適当に触った感じだと正常に動作していそうですが、動作は保証できないです :bow:

TranslucentActivity.kt
class TranslucentActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
            val targetSdkVersion = applicationInfo.targetSdkVersion
            applicationInfo.targetSdkVersion = Build.VERSION_CODES.O
            super.onCreate(savedInstanceState)
            applicationInfo.targetSdkVersion = targetSdkVersion
        } else {
            super.onCreate(savedInstanceState)
        }
        setContentView(R.layout.activity_translucent)
    }
}

まとめ

targetSdkVersionを27に上げた場合、APIレベル27(Android8.1)で動作確認すれば大丈夫かと思いきや、そうでもないようですね :sweat_smile:
とはいえ今回の場合はActivityを起動した瞬間にクラッシュするので、自動テストを充実させて防ぎたいものです :muscle:

何かご意見ご感想、間違い指摘などがあればコメントください :dancer:

recruitlifestyle
飲食・美容・旅行領域の情報サイトや『Airレジ』などの業務支援サービスなど、日常消費領域に関わるサービスの提供するリクルートグループの中核企業
http://www.recruit-lifestyle.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away