Composeのstate監視がどこまでされるのか試してみた
背景(?)
Jetpack Compose(以下Compose)ではState<T>
というものがあります。
これはComposeにおいて状態を管理するためのもので、ComposeはこのState<T>
の変更を監視し値が変更されるとそのStateを利用しているComposableを自動で再読み込みして値の変更を見た目に反映させます。
詳細は以下リンク先にあります。
そしてこの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の部分まで値が更新されました。
コードを生成するコードを作成して、変数をいくつまで変更検知できるか試してみました。
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
となってしまい検証はできませんでした。
これらのことから、ビルドできる範囲内であれば変数がいくつあっても変更検知できそうですので、特に数は気にせず利用できそうです。
Discussion