前置き

久しぶりの更新。
android8.0でSDKバージョンを27以上にすると特定条件下でアプリがクラッシュする現象を確認したので原因と対策をメモ。

環境

node.jsのバージョン

node -v
v10.5.0

react nativeのバージョン

react-native-cli: 2.0.1
react-native: 0.57.8

経緯

なぜこの現象に遭遇したか

個人でアプリ開発をしているときにgoogleから2018年8月からSDKの最低バージョンが26になるよ〜のアナウンスが出たから。
アナウンスが出たので、せっかくなら最新に上げておこうと思って上げたらこのザマです。

他のandroid端末でも確認してみたけど、他は大丈夫だったからなおのこと不思議だった。
・確認したOSのバージョン
android 7.1
android 8.0
android 9.0

原因

android8.0のバグだった

原因を調べているうちに以下のサイトに辿り着いた。
https://qiita.com/oxsoft/items/042aaa6e9c7ae828e967

まさに遭遇している問題と同じ!!
神はここにいたのか!!

上記のサイトにも記載されているが改めて記載する。
以下の4つの条件がすべて揃った場合にアプリがクラッシュする。
・Android 8.0の端末である
・targetSdkVersionを27以上にしている
・背景を透過にしている
・画面の向きを固定している

・Android 8.0の端末である

該当箇所は上記のサイトから転載

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

        // ...
    }
}

少なくともandroid8.1では削除されているらしいので仕様ではなくバグだと思われる。

・targetSdkVersionを27以上にしている

以下のファイルが対象(react nativeで作成したアプリの場合)
android/build.gradle

buildscript {
    ext {
        buildToolsVersion = "27.0.3"
        minSdkVersion = 16
        compileSdkVersion = 27
        targetSdkVersion = 27
        supportLibVersion = "27.1.1"
    }
        ・・・
}

・背景を透過にしている

以下のファイルが対象(react nativeで作成したアプリの場合)
android/app/src/main/res/values/styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="android:windowIsTranslucent">true</item>
    </style>

</resources>

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

以下のファイルが対象(react nativeで作成したアプリの場合)
android/app/src/main/res/values/styles.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ・・・

    <application
      android:name=".MainApplication"
      android:allowBackup="true"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:theme="@style/AppTheme">
      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:launchMode="singleTop"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:windowSoftInputMode="adjustResize"
        <!-- 縦向きで固定部分 -->
        android:screenOrientation="portrait">
        ・・・
    </application>

</manifest>

対策

・Android 8.0の端末である

これに関してはどうしようもない。
android8.0を動作保証外にするしかないがユーザーに怒られそう・・・

・targetSdkVersionを27以上にしている

targetSDKのバージョンを26で固定すれば回避はできる。
しかし、最低バージョンは年々上がって行くみたいなのでいずれは対応しなければ行けない気がする。

・背景を透過にしている

背景の透過をやめる。
これは、まぁアプリのデザイン次第でどうとでもなる気がする。
別に透明である必要性もないし。

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

画面の回転に対応させる。
これもアプリ次第ではあるけど地味に難しいと思う。
回転に対応させると工数も増えそうだし・・・

もう1つは「portrait」じゃなくて「behind」にする。
これが1番現実的な気がする。

まとめ

android8.0の変な箇所でハマるとは思ってなかった。
googleさんもやらかしてくれたもんだぜ。

ちなみにreact-native initでアプリを作成した場合はandroid8.0にしか該当しない。(2019年1月現在)
今後最低バージョンが上がればsdkが27以上にも該当するが、それだけではクラッシュしないのでセーフ!