前言
※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
}
終わり
全然関係ないですけど、改行がうまく効かなくて、見にくいと改めて感じました。