hiro99ma blog

何か技術的なこと

android: ViewModel の Codelabs がさっぱりわからない

2024/10/19

ViewModel に関する Codelabs があったのでやっている。

Compose での ViewModel と状態

ViewModel の追加

おもしろいくらい、何を言っているかわからない。。。
Codelabs のユニット 3 をスキップしてユニット 4 からやっているから見逃しているのか?

StateFlow

StateFlow は、現在の状態や新しい状態更新の情報を出力するデータ保持用の監視可能な Flow です。

例えが合っているか分からないが、こういう風にメソッドが自分自身を返して最後に.build()で一気に構築するのが cold flow っぽい感じだろうか。

hot は下流に流すときには既に加工済みになっているタイプ、例を出すなら・・・ChaCha20 みたいなストリーム暗号とか?

cold だとデータを使いたいときに一度完了させないといけないが、それだと途切れてしまって不便なので UI では hot flow の方が向いているという解釈で良いのかしら。
hot flow は上流の値を放り込める Mutable なやつと、その read only 版がセットで存在する。
Kotlin の一覧では MutableSharedFlowMutableStateFlow の 2つだけのようだ。

ほとんど読んでいないが、下流が 1つなら StateFlow、複数なら SharedFlow という訳でもなさそうだ。
StateFlow.shareIn()SharedFlow を返すようである。
逆はなさそうなので、とりあえず StateFlow を使っておけばよいのだろうか。
StateFlowは hot といいつつ shareIn を使用したコールド Flow のホット化 だったり、機械翻訳だとちょっと怪しいのかもしれない。

細かいところはさっぱり分からんが、とりあえず使ってから考えよう。

Backing Property

バッキング プロパティを使用すると、そのオブジェクト自体ではなくゲッターから返すことができます。

ここでは MutableStateFlow の変数 _uiStateuiState という Backing property にしている(用語の使い方がよくわからん)。 getter として使う場合は read only にするため .asStateFlow()StateFlow に変換している。

GameUiStateクラスは currentScrambledWord(String型) だけをプロパティとして持っている。 val なので初期化したら変更できない。
これを Flow として使うのでこうしたのだろうが、それなら String でもよかったんじゃないのと思った。
これから拡張するのか、class の方があとから拡張できて便利とかだろうか。

Compose UIの設計

6. Compose UI を設計する

ViewModelの使い方がここで分かるはず。。。

元になる英単語は allWords に固定値で入っている(配列ではないが)。
ゲームの課題として出す単語は allWords からランダムで 1つ選び、文字を並べ替えた文字列を GameViewModel.currentWord で保持している。
currentWord を更新するのは GameViewModel のコンストラクタか resetGame() が呼ばれたとき。
作った課題の文字列はusedWords()に追加するし、その文字列が入ったGameUiStateオブジェクトを作って GameViewModel.uiState.value に代入している。
これがおそらく MutableStateFlow の上流からデータを流し込む操作になっているのだろう。
下流をたどると、GameScreen.gameUiState.currentScrambledWordGameLayout に渡り、そのまま Text に使われている。
これで、GameViewModel.uiState を変更すると Text に設定されている文字列が自動的に置き換わるのだろう。

image

なんかすごいね。
ボタンを押して次の課題に進むまで変わらないんだからそのとき更新すればいいんじゃないの、と私なんかは思うのだけど、まあこれはそういう課題だし。
それよりも、こういうしくみがどうやって動作しているのかさっぱり理解できんのだ。

どこかで値が変わったことか代入されたことを監視しないと表示への反映までできないと思うのだ。
@Composable は再コンポーズによってしかコンテンツが書き換えられないというルールだし、そういう機構があるのだろうけど、ずっと監視するのは大変だよなあとか、再コンポーズするような操作がぱらぱらと行われたら画面がちらつかないかなあとか、そういうことを心配してしまう。
まあ、それでも困らないように作られているんだろうけどさ。

今回はスクランブルされた文字列 1つだけだが、他にも渡したければ GameUiState の中身を増やせば良い。
違うタイミングで更新するようなデータであれば data class を追加して同じように StateFlow の経路を増やすことになるか。

こういう、構造的な作り方を自然と思いつくようにならないといかんのだろうが、ちょっと難しい。
最初からアプリのテンプレートがそうなっていれば、そういえばそうだった、と思いつくかもしれない。
サンプルがあるので、上手につきあっていきたいものだ。

< Top page