Composeのstate監視がどこまでされるのか試してみた
環境
- Jetpack Compose 1.0.0-beta06
- Gradle 7.0.0-alpha15
- Kotlin 1.4.32
背景
Jetpack Compose(以下Compose)ではState<T>
というものがあります。
これはComposeにおいて状態を管理するためのもので、ComposeはこのState<T>
の変更を監視し値が変更されるとそのStateを利用しているComposableを自動で再読み込み(recomposition)して値の変更を見た目に反映させます。
詳細は以下リンク先にあります。
そしてこのStateは以下のように使うこともできます。
class TodoViewModel : ViewModel() { // private state private var currentEditPosition by mutableStateOf(-1) // state var todoItems by mutableStateOf(listOf<TodoItem>()) private set // state val currentEditItem: TodoItem? get() = todoItems.getOrNull(currentEditPosition) // ..
コードは以下Codelabから引用
このコードではcurrentEditPosition
はInt
・todoItems
はList<TodoItem>
として利用できますが、by mutableStateOf
を使って定義しているため、Composeの監視対象とされています。
そして、currentEditItem
はこの2つの状態を利用しています。
こういった書き方をすることで、ComposeはcurrentEditPosition
とtodoItems
の変更を監視しつつ、どちらかに変更があったタイミングでcurrentEditItem
のゲッターを再度呼び出し見た目に反映させます。
注意点としては、値が変更されたことをトリガーにしたい値(currentEditPosition
やtodoItems
)をby mutableStateOf
や= mutableStateOf
でStateオブジェクトとして定義する必要があります。
本題
ここで、この状態をゲッターで利用している値の監視はどこまで継続されるのか気になったため以下のコードの動作を確かめてみました。
コード
class MainViewModel : ViewModel() { var count1 by mutableStateOf(0) private set var count2 by mutableStateOf(0) private set val countResult1: Int get() = count1 + count2 val countResult2: Int get() = countResult1 + 1 val countResult3: Int get() = countResult2 + 1 val countResult4: Int get() = countResult3 + 1 val countResult5: Int get() = countResult4 + 1 val countResult6: Int get() = countResult5 + 1 val countResult7: Int get() = countResult6 + 1 val countResult8: Int get() = countResult7 + 1 val countResult9: Int get() = countResult8 + 1 val countResult10: Int get() = countResult9 + 1 val countResult11: Int get() = countResult10 + 1 val countResult12: Int get() = countResult11 + 1 val countResult13: Int get() = countResult12 + 1 val countResult14: Int get() = countResult13 + 1 val countResult15: Int get() = countResult14 + 1 val countResult16: Int get() = countResult15 + 1 val countResult17: Int get() = countResult16 + 1 val countResult18: Int get() = countResult17 + 1 val countResult19: Int get() = countResult18 + 1 val countResult20: Int get() = countResult19 + 1 val countResult21: Int get() = countResult20 + 1 fun onClickCount() { count1++ } fun onClickCount2() { count2++ }
@Composable fun Main() { val viewModel: MainViewModel = viewModel() Column { Text(text = "Count1:${viewModel.count1}") Text(text = "Count2:${viewModel.count2}") Spacer(modifier = Modifier.height(10.dp)) Text(text = "Result1:${viewModel.countResult1}") Text(text = "Result2:${viewModel.countResult2}") Text(text = "Result3:${viewModel.countResult3}") Text(text = "Result4:${viewModel.countResult4}") Text(text = "Result5:${viewModel.countResult5}") Text(text = "Result6:${viewModel.countResult6}") Text(text = "Result7:${viewModel.countResult7}") Text(text = "Result8:${viewModel.countResult8}") Text(text = "Result9:${viewModel.countResult9}") Text(text = "Result10:${viewModel.countResult10}") Text(text = "Result11:${viewModel.countResult11}") Text(text = "Result12:${viewModel.countResult12}") Text(text = "Result13:${viewModel.countResult13}") Text(text = "Result14:${viewModel.countResult14}") Text(text = "Result15:${viewModel.countResult15}") Text(text = "Result16:${viewModel.countResult16}") Text(text = "Result17:${viewModel.countResult17}") Text(text = "Result18:${viewModel.countResult18}") Text(text = "Result19:${viewModel.countResult19}") Text(text = "Result20:${viewModel.countResult20}") Text(text = "Result21:${viewModel.countResult21}") Button(onClick = viewModel::onClickCount) { Text(text = "count") } Button(onClick = viewModel::onClickCount2) { Text(text = "count2") } } }
結果
このコードを実際に動かしてみたときの動きは以下のようになります。
2つのボタンいずれかを押すとResult1〜Result21の部分まで値が更新されました。
この結果からstateを使っているゲッターの監視は少なくとも20個くらいでも機能していることがわかります。
実際にどれくらいの数でも監視が可能なのかはたまた無制限なのかまでは調べられていませんが、大体のアプリケーションはここまで伝播させるケースは無いかと思いますので、「このゲッターは監視対象になるのか?」ということはほとんど気にしなくても良さそうです。
ちゃんといくつまで変更検知できるか試してみようかとは思ったのですが、大変そうだったので20個くらいでやってみました。
もし試した方がいれば教えてください。
2021/05/06 16:30頃追記
コードを生成するコードを作成して、変数をいくつまで変更検知できるか試してみました。
生成コードpythonで書きましたが、特に理由はないです。
f = open('foo.txt', 'w', encoding='UTF-8') for i in range(8000): f.write('val countResult%s: Int\n' % (i + 1)) f.write('get() = countResult%s + 1\n' % (i)) f.close()
結果として確認できたのは8000個の変数まで変更検知されていました。
それ以上の変数(8500個以上など)も試してみましたが、そちらはビルド時にClass too large
となってしまい検証はできませんでした。
これらのことから、ビルドできる範囲内であれば変数がいくつあっても変更検知できそうです。
追記ここまで
mito.