Flutter-タイマーアプリケーションを作ってみる5

実装時や実装後に問題となった点について。

想定していた機能がなかったり、Dozeを知らなかったためちょっといろいろ試行錯誤をしてしまった。

仕様変更した内容

発音関連

仕様では変更しているのだけど、当初の仕様ではシステムサウンドを設定することを前提に考えていた。
しかし、システムサウンドをユーザーに指定させるという機能はどうやら用意されておらず、自分で音源ファイルを指定して音を鳴らすか、警告or通知音というシステムで用意されている2種類の音を鳴らすということしかできなかった。

結果、音を鳴らすという要求を満たすため、以下のパッケージを使用するようにした。

flutter_ringtone_player | Flutter package
A simple player for system default ringtones, alarms and notifications

音の種類は通知音のみで、音の長さだけ指定可能なように変更した。

Doze問題

連続稼働するAndroidアプリを作ったのが今回初めてで、タイマーを実機で動かしていた時に、端末がスリープ状態に移行し、音が鳴る予定時刻にならなかったという状態になった。

この時以下の2種類の症状が出たので余計混乱したというのがあった。

  1. スリープ後、1分ぐらいたった後に立ち上げてみると、スリープ後タイマーが+30秒までしか進んでいなかった。
  2. スリープ時に、指定したタイマー時間経過しても、音が鳴らなかった。

どちらもDozeモードに入ったのが原因。

タイマーが+30秒しか進んでいなかった問題

これに関しては、Stopwatchクラスを使用していたのが問題だったようだ。

Stopwatchの時間の取得はVMのOS::GetCurrentMonotonicTicks()で行われている。
Android実装ではclock_gettimeをCLOCK_MONOTONICで呼び出している。この呼び出しは、明示的にされているドキュメントはないものの、Dozeの影響を受けカウントが停止されてしまうようだ。

Attention Required! | Cloudflare

上の2番目の回答参照。

上記理由により、Stopwatchの使用をやめて現在時刻をウォッチする方法に変更した。

音が鳴らなかったへの対応

これは結局対応という対応はできず、結果電源の最適化をOFFにするという対応になった。
なので、スリープさせる場合は給電中じゃないとこのタイマーアプリは使用できない。(音が鳴らない)

Dozeの調査の内容

Doze回避できないかいろいろ調べてみたのだけど、普通のAndroidアプリを作っていても難しい(機種により可否あるらしい)ということなので、結局完全対応はあきらめることにした。

その際、バックグラウンドを使えばいいのではないかと思い、アプリをバックグラウンド対応したのだけど、結局その実装は破棄してしまった。

それがgithub上のこちらのブランチ。

GitHub - take4blue/stacktimers at スレッド化タイマー
Contribute to take4blue/stacktimers development by creating an account on GitHub.

タイマー処理をViewModel側から切り出しそれをバックグラウンド側に持っていき、進捗状況をStreamでやり取りするような感じにした。その際、そのままではバックグラウンド側の処理をUnitTestできなかったのでFlutter-flutter_backgroundのUnitTest用の実装みたいなものも作ったのだけど、まあ無駄になってしまった。

実装の変更

時間を設定するためのドラムロールダイアログ

自前で部品を作りそれを使用する予定だったのだけど、結果的にやめて、以下を使うようにした。

flutter_picker | Flutter package
Flutter plugin picker. Include NumberPicker, DateTimePicker, ArrayPicker, and default linkage Picker. Provide flexible p...

自前の部品で実装まで完了したのだけど、時間設定の:(コロン)の表示がうまくできなかったので自分部品の使用を中止した。

FutureBuilder使用の停止

ListView系列を使用している箇所に関してFutureBuilderを使用したウィジェットの構築方法を取りやめた。

理由についてはFlutter-FutureBuilder考察を参照。

データベースアクセスはasyncな関数のためデータを取りそろえる関数はFutureになる。そのためFutureBuilderを使うのは当然と思っていた。

しかし実機でテストをしていたところ、データ取り出し時に「データ取り出し中」のウィジェットがちらっと出てしまうので、なんか目に悪い感じがしてしまった。

そのため、FutureBuilderを使わない実装に変更し、データが取り揃えられた時点でウィジェットを再構築するように更新処理を入れるようにすることとした。

この部分の処理は、TimerEditPageクラスのbuilderメソッドの部分。ここでしょっぱなvm.loader();を呼び出しデータのローディングを開始するようViewModel側に指示を出す。
しかしこの時点ではデータは揃えられていないのでとりあえずListViewに表示されるデータ個数は0となる。

その後ViewModel側でデータの取得を完了した時点でView側の更新処理(update命令)を行うことで正しいデータでの表示がされる。

本来であればFlutter-FutureBuilder考察で使ったウィジェットを使った方がいいのかもしれないが、今回のケースではデータ取得に要する時間がとても短いと分かっているので、上の様な実装にした。

最後に

それほど難しくないアプリだったので1~2週間ほどでできるかなと思ったのだけど、いろいろ試行錯誤して作っていったら1か月ちょいかかってしまった。

仕様変更するとテストとかも変更されるで、そこで地味にやる気がそがれてしまったのも一つの要因。2~3日実装するのを止めてしまったり。

コメント

タイトルとURLをコピーしました