前言
※AndroidのdevelopersサイトからUI architecture読んで、気になる部分をメモする感じです。
今回はlifecycleを拝見してます。
https://developer.android.com/jetpack/compose/lifecycle
キーワード
-
Composable:@ComposableをつけてるClass、UIのパーツ -
Composition:Composableから生成され、UIを記述するツリー構造 -
Compose:Jetpack Composeのシステムを指してる
メモ
Lifecycle overview
-
唯一
Compositionを変更する方法はrecomposition。 -
Composableのlifecycleはすごくシンプルで、以下の三つしかない。-
Compositionに入場 - recomposeしないか、recomposeする
-
Compositionから退場
-
-
recompositionは
State<T>の変更でトリガーされる。
Composableが複数回に呼ばれたときに、複数のインスタンスがCompositionに設置される。
@Composable
fun MyComposable() {
Column {
Text("Hello")
Text("World")
}
}
Anatomy of a composable in Composition
recompositionの時、Composableは呼ばれたかどうかで判断される。
もしComposableが前回のCompositionと今回も両方呼ばれた時はinputsが変わったかで判断される。
Add extra information to help smart recompositions
複数回Composableを呼ぶ場合は実行順序を識別子としてComposableを区別する。ただ、場合によって実行順序がうまく動作しない場合がある。
@Composable
fun MoviesScreen(movies: List<Movie>) {
Column {
for (movie in movies) {
// MovieOverview composables are placed in Composition given its
// index position in the for loop
MovieOverview(movie)
}
}
}
こちらを例にすると、Listの最後に新しい項目を追加する時に、最後の項目以外の項目は同じ実行順序を使ってるため、recomposeしない。
ですが、Listの最前、途中に項目を追加すると、それ以外の項目も影響され、多数のComposableがrecomposeすることになる。
この場合は提供されてるkey()を使って、識別子を指定することができる。
@Composable
fun MoviesScreenWithKey(movies: List<Movie>) {
Column {
for (movie in movies) {
key(movie.id) { // Unique ID for this movie
MovieOverview(movie)
}
}
}
}
また、lazyListにもDSLで提供してる
@Composable
fun MoviesScreenLazy(movies: List<Movie>) {
LazyColumn {
items(movies, key = { movie -> movie.id }) { movie ->
MovieOverview(movie)
}
}
}
Skipping if the inputs haven't changed
もしComposableが既にCompositionにある場合、全てのinputsが安定してるなら、recomposeがスキップできる。
そして、以下の型は@Stableを明示しなくても、安定性があると扱われてる。
- すべてのプリミティブ値型: Boolean、Int、Long、Float、Char など
- 文字列
- すべての関数型(ラムダ)
ただし、唯一可変型だけど安定性があるのがMutableState。
またComposeが安定性を区別できない場合がある。例えばinterface、公開プロパティが可変として、仮に実装で不変の値を使っても安定してると扱いされない。その場合は@Stableを使ってComposeに指定できる。
// Marking the type as stable to favor skipping and smart recompositions.
@Stable
interface UiState<T : Result<T>> {
val value: T?
val exception: Throwable?
val hasError: Boolean
get() = exception != null
}
終わり
全然関係ないですけど、改行がうまく効かなくて、見にくいと改めて感じました。