特定の端末で特定の写真を開くとエラーも出ずすっとアプリが再起動する。
OpenGLの問題で同じようなこともありましたが今回はより条件がわからない。
結果的におそらくメモリ不足であろうという推測に至ったので、ImagePicker
で取得する画像の調整や使えるメモリ量などの備忘録。
最終的にImagePickerが原因で対処療法しかないという内容ですが、これを機に色々調べることが出来たので時系列順に書いていきます。
エラー?箇所
エラーも出ず落ちているのでエラー個所というのも変かもしれませんが、ここで止まってるなという場所はすぐに特定できました。
await ImagePicker.launchImageLibraryAsync()
です。
ここで{allowsEditing: true}
を指定しているにもかかわらず、編集画面に入らずアプリが再起動しています。
ただこの関数自体はちゃんと動いてますし、catch
などでも補足できませんでした。
経緯と推測
もともとテスト用実機のうちの1つで遊んでいるときに、昔の写真を選択するとアプリが落ちました。
どうやら画像によって発生するようなので、同じ挙動の画像を探して画像形式や縦横比率、サイズなんかをメモって原因を探していました。
そんな中、すでにダメだった画像をもう一度開いてしまい、しかも今度は成功するということがありどうもメモリ不足なんじゃないかと疑い始めます。
launchImageLibraryAsync()
はオプションでquality
を指定することが出来ます。
FileSystem
のAPI
も使って実際に抜き出した画像ファイルのサイズを確認します。
1 2 3 4 5 6 7 8 9 10 11 12 |
const r = await ImagePicker.launchImageLibraryAsync({ allowsEditing: true, aspect: [1, 1], quality: 1.0 }).catch(e => { console.log(e); }); if (r && r.cancelled === false) { const info = await FileSystem.getInfoAsync(r.uri, { size: true }); console.log(info.size); } |
読み込んだ画像の容量確認
テスト1:落ちたり落ちなかったりする画像
3.80MBの写真が{quality: 1.0}
で6.03MB、{quality: 0.5}
で0.56MBになります。
テスト2:確実に落ちる画像
4.07MBが{quality: 1.0}
で7.10MB、{quality: 0.0}
で0.19MBでした。
ドキュメントノートにもありますが、比率1:1
でトリミングしているのに元画像より大きくなっています。jpg
でこの容量なので実際に使う時にはもっと大きそう。
とはいえアプリが落ちるほどではないような。
{quality: 0.0}
なんて大丈夫かと思うかもしれませんが、今回の用途だとスマホ解像度レベルの画像になるのでそこまで変化はなかったです。以下は320x320
。
もちろん解像度の高い端末のことを考えると0.0
にするべきではないです。
この辺をどうするべきみたいな話は見当たらなかったので{quality: 0.5}
くらいでお茶を濁すのが妥当かもしれない。それでも1/10
になりますし。
ただどうしてもネイティブな編集UIが出るタイミングで落ちることがある。
もし端末メモリに合わせて処理を変えるのならDevice
モジュールを使いましょう。
Device.totalMemory
でデバイスの総メモリ(bytes
)、Device.getMaxMemoryAsync()
でJVM(ART?)の使用できるメモリ量(bytes
)が得られます。
さらに深堀り
-
AndroidManifest
でlargeHeap
オプションを付けることで落ちにくくなるらしいがExpo
ではデフォルトでtrue
になってそう(テンプレート) - 同じ画像でも「最近」から取得するとこのエラーは起きない!
- 「ギャラリー」でファイル数の多いフォルダを選択しスクロールしてすべて表示したのちに画像を選ぶと確実に落ちる
あからさまな容疑者が出てきました。
ImagePikcer選択時に本体が落ちている?
ファイル数が500くらいあるフォルダの下の方(スクロールして表示させる方)がアプリが落ちる画像でした。
試しにスクロールしてから上へ戻して選択するとどの画像でも同じ現象が起きます。
サムネイルを大量に読み込んでメモリが足りなくなったから呼び出した本体だろうと自動で落としてるとかなんだろうか。
エラーやクラッシュレポートが出ないことから考えてもこれじゃないかな。
最大の問題はこれの対処法がおそらくないこと。
javaソースコードをたどってみるもライブラリ的に問題があるわけではないもんな。
アプリ自体が落ちるので返値(uri
)をもった状態で起動というのも無理。
メモリの小さな端末で大きなフォルダから選ばないように(画像自体を他に移したりするように)してもらうしかなさそうです。
※Huawei独自の「アプリの保護」という機能によってバックグラウンドアプリが終了するような内容も見かけましたが、今回は関係なかったです(落ちない設定になってた)。