😸

Flutterのライフサイクルでアプリが落ちた時にAPI通信が出来ない現象が謎すぎ

2023/01/08に公開

皆さんご存知の、WidgetsBindingObserverでは、
アプリのライフサイクルと言われるものを管理する事ができるみたいですね。

僕もアプリの中で、ユーザーがオンライン中なのか、オフライン中なのかを把握するために
アプリを『付けた』『消した』というのを、まぁまぁ正確に管理する必要があって、
WidgetsBindingObserverを使ったら行けんじゃね?みたいな感じで思ってました。

ただ、実際にテストしてみると謎にAPI通信が処理されません。

case AppLifecycleState.detached:

特にこいつが問題児で、全くAPI通信をしようとしないんですよね。
・アプリが落ちた段階で、API通信をする発想がダメなのか
・非同期処理をそもそも付けつけていないのか
見当もつかず泣きそうです。

まぁ、ちょっと冷静に考えればアプリが落ちた時点で非同期処理を強要するなんて
S○X終わった後すぐにもう一戦みたいなノリなので
確かにあまり良くないのかもしれませんね(少なくとも僕は無理です)

ということで実験してみました。

どういうのだったら動くのだろうか。

1つ目
case AppLifecycleState.detached:
int index = 0;
for (var i = 0; i < 10000; i++) {
   index++;
   print('index: $index');
}

これはまさかの動いた
ちゃんとprint関数で1万まで行ったので、あぁ結構行くじゃんと思った。
イメージ感としては、1000回くらいの処理で1秒なので、
10,000なら10秒くらいあるのかなと思って

次にこれを試した

2つ目
case AppLifecycleState.detached:
int index = 0;
for (var i = 0; i < 10000; i++) {
   await Future.delayed(const Duration(milliseconds: 10));
   index++;
   print('index: $index');
}

まさかの1回もprint関数が発火せんかった・・・。
なんじゃこれ。

確かにasyncという文字がないのに
awaitでを突っ込んでもエラーが無いから変だなぁとは思っていた。

じゃあ、await を打たずにやってみるか...

3つ目
case AppLifecycleState.detached:
int index = 0;
for (var i = 0; i < 10000; i++) {
   Future.delayed(const Duration(milliseconds: 10));
   index++;
   print('index: $index');
}

こいつは動くんかい
くっそ謎・・・。

じゃあ、3つ目のノリでAPI通信を打てば問題なさそうな気がする

4つ目
case AppLifecycleState.detached:
print('start');
ref
   .read(firestoreProvider)
   .collection('tests')
   .doc('testsebes')
   .set({'test': 'test'});
print('完了1');

testsというコレクションに適当なデータを突っ込もうとした結果ですが、
まさかの動かん。くっそ謎。なんでや。ウゼェぇぇ。

まぁ、シャーネ。多分いろいろと噛み合わん事があるんやろう。

API通信が謎に出来ないので、別の解決策を考えるしかない。
という事で、RealTimeDatabaseで考えるか。

RealTimeDatabase アプリが落ちた時の処理
await FirebaseDatabase.instance.ref().child(userId).update({'isOnline': false});

後は、functions発火させたらFirestoreにデータが突っ込まれるだろう。

個人的には、RealTimeDatabaseはほぼ全く勉強していないし、
興味も湧かないので、やりたくなかったけど、しょうがない。動かんもん。

暴力的な結論

WidgetsBindingObserverのdetachedではAPI通信は辞めときな。

という結論に僕はなったが、本質的なエラーや問題が分からないので
正直、かなり暴力的な結論だと思っています。

だれかコーヒー奢るから教えてmmmmm

Discussion