注意!
この記事はQiitaにて公開されていた内容をimportしたものです。
これらの内容は場合によっては陳腐化していて役に立たなくなっていたり、有害であったり、現在の著者の主張と異なることがあります。
皆様の判断の上でご利用いただけますと幸いです(度を超してヤバいものは著者に連絡して頂ければ対応します m(_ _)m)
はじめに
これまでAndroidネイティブアプリを主に担当していた自分が、紆余曲折を経てReact Nativeを担当することとなりました。
まず、今年(2018年)11月までに targetSdkVersion 26
対応をする必要があったので、すぐさま対応することとなりました。
ただ、問題がないならできるだけtargetSdkVersionをあげておきたかったので、targetSdkVersion 28
にしつつ、諸々の対応をしていくこととなりました。
Pie環境でデバッグが動かない
ひとまずAndroid9環境をエミュレーターで用意し、 react-native run-android
しましたが、以下のエラーが表示されます。
unknown:ReactNative E Exception in native call
E java.lang.RuntimeException: Unable to load script from assets 'index.android.bundle'. Make
sure your bundle is packaged correctly or you're running a packager server.
E at com.facebook.react.bridge.CatalystInstanceImpl.jniLoadScriptFromAssets(Native Metho
d)
E at com.facebook.react.bridge.CatalystInstanceImpl.loadScriptFromAssets(CatalystInstanc
eImpl.java:216)
E at com.facebook.react.bridge.JSBundleLoader$1.loadScript(JSBundleLoader.java:33)
E at com.facebook.react.bridge.CatalystInstanceImpl.runJSBundle(CatalystInstanceImpl.jav
a:234)
E at com.facebook.react.ReactInstanceManager.createReactContext(ReactInstanceManager.jav
a:1137)
E at com.facebook.react.ReactInstanceManager.access$900(ReactInstanceManager.java:113)
E at com.facebook.react.ReactInstanceManager$5.run(ReactInstanceManager.java:944)
E at java.lang.Thread.run(Thread.java:764)
おおーん?これでかなり悩みました。 チームメンバーはこの問題にめったに遭遇しないと言うし、別の実機(あとで気づいたのですが実機はAndroid8以下です)では正常に動作する。 しかし、Pie環境でどうしても動作確認をしておきたかった。
react-native bundle コマンドで対応する方法 もあるんですが、これは実質React Nativeのビルドチェーンでやってくれていることを自分でやっているだけだったので、とりあえず無理矢理動かす時だけ使いました。
結論: networkSecurityConfig
の未指定が原因
Android Nからの機能で Network Security Configuration があります。 当初はクリアテキスト トラフィック(非HTTPS通信)を認めていたのですが、Android Pieからめでたく(不幸にも?w)クリアテキストをデフォルトでブロックするようになりました。
Network Security Configurationのデフォルトの値を見比べると、確かにPie (API 28)から cleartextTrafficPermitted="false"
となっています。
そして、React Nativeのデバッグビルドでは、スマホの実機(エミュレーター)と母艦(パソコン)間で通信をします。 その通信がクリアテキストで行われるため、通信がブロックされている、という状況です。
なお、この状態は(↑でもちらっと書きましたが)、現時点においては、Android Pie環境かつアプリが targetSdkVersion 28
となっているときに発動します。
解決策: networkSecurityConfig
を実装する
コードです。
...
<application
android:name=".MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
>
...
android:networkSecurityConfig
を<application>
に記述します。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<!-- for React Native Hot-reloading system -->
<domain includeSubdomains="true">localhost</domain>
</domain-config>
<base-config cleartextTrafficPermitted="false" />
</network-security-config>
network_security_config.xml
の中身はこれ。
includeSubdomains
はtrueにしてありますが、falseでも良いかも知れません。
他にもクリアテキストな通信が発生するなら、いろいろ定義を足す必要があります。 (今回の対応の際に、やむを得ずクリアテキストな通信が発生する箇所がブロックされていることに気がつきましたが、iOSでは先んじてクリアテキストの制限があったため、迂回するためのホスト定義をコピーして対応出来ました)
まとめ
まさか networkSecurityConfig
が原因でダメになっているとは夢にも思わず、原因究明がめちゃ遅れました。
React Nativeはバージョン毎に対応しているtargetSdkVersionが明示されているので、そこから逸脱しないようにしていれば大やけどしない・・・かも知れない。