公開日: 2024/03/01
Jetpack ComposeのStrong Skipping Mode
は開発者にとって嬉しい機能である一方、Composeの深い知識が必要なため理解が難しいと思います。
以下の記事で詳細に説明されていますが、前提知識が必要な箇所もあるため、本記事でわかりやすく解説します。
https://medium.com/androiddevelopers/jetpack-compose-strong-skipping-mode-explained-cbdb2aa4b900
下記を可能にする機能のことです。
順番に解説していきます。
これを理解するためにはStability(stable/unstable)について理解する必要があります。以下の記事で解説しているのでぜひ参考にしてください。
https://yusuke-suzuki.site/post/20240229_compose_stability
今まで(=Strong Skipping Modeが無効な場合)はRecompositionをスキップするためには、パラメータをstableにする必要がありました。
Composeにおいてパフォーマンスの問題が発生したときに、下記のような取り組みでパラメータをstableにした経験がある人は少なくないと思います。
Strong Skipping Modeを使用するとunstableなパラメータでもRecompositionをスキップできるようになります。
記事中のコードを引用して具体例で説明します。
@Composable
fun CollectionScreen(viewModel: CollectionViewModel = viewModel()) {
var favorite by remember { mutableStateOf(false) }
Column {
FavoriteButton(isFavorite = favorite, onToggle = { favorite = !favorite })
ArticleList(viewModel.articles)
}
}
@Composable
fun ArticleList(
articles: List<Article>, // List = Unstable, Article = Stable
modifier: Modifier = Modifier // Stable
) {
// …
}
Strong Skipping Modeが無効であればFavoriteButton
のonToggle
が実行されたとき、ArticleList
はRecompositionされてしまいます。(ArticleList
はunstableなパラメータarticles
をもつため)
Strong Skipping Modeを有効にすると、articles
が変更されていないのでArticleList
のRecompositionをスキップすることができます。
stableなパラメータはObject.equals()
(==
)でパラメータの変更の有無を判断していますが、unstableなパラメータはどのようにパラメータの変更有無の判断をしているのでしょうか?
unstableなパラメータは===
によって変更の有無を判断するようです。つまりインスタンスのメモリの参照先のアドレスを比較して、同じインスタンスであるか比較します。
今まで(=Strong Skipping Modeが無効の場合)、Composable関数のラムダ内でunstableな変数を参照していると(=キャプチャ)、Recompositionをスキップすることができませんでした。
例えば以下のComposable関数があったとき、viewModel
がunstableであればRecompositionのたびにラムダが再生成され、NumberComposable
もRecompositionをスキップできませんでした。
@Composable
fun NumberComposable(
current = number,
onValueChange = { viewModel.numberChanged(it) }
) { ... }
このラムダの再生成を防ぐために以下のような取り組みをしていた人は少なくないと思います。
remember
するStrong Skipping Modeを有効にするとラムダがunstableな変数を参照していても、ラムダが再生成されなくなります。
内部的にはComposeコンパイラがラムダをremeber
でラップすることにより再生成を防いでくれるようです。
@Composable
fun MyComposable (unstableObject: Unstable ,steadyObject: Stable ) {
val lambda = remember(unstableObject,steadyObject) {
{
use(unstableObject)
use(stableObject)
}
}
}
(unstableなkeyは===
を使用して比較され、stableなkeyはObject.equals()
を使用して比較されるので、厳密には通常のremember
ではない)
Composeのパフォーマンスを考慮しだすと、Composeの深い理解やStabilityを意識したコードにしなければいけないのは、よくないよねーということなのでしょう。
記事中で以下のような文章があり、Composeの開発者も課題に思っていたようです。
We don’t want you to have to be experts in Compose internals in order to write good Compose code!
(翻訳: 良いComposeコードを書くために、Compose内部の専門家である必要はありません!)
今までもパフォーマンスの問題がなければStabilityなど意識しなくて良い内容ではありましたが、Strong Skipping Modeのおかげで実装するときに、考えることが減るのはとても大きなメリットだと思いました。
Recompositionして欲しいのにされないという問題が発生する可能性もはらんでいるので、慎重に進めるというのも納得しました。
以上です!参考になればぜひシェアをお願いします!
ラムダ外の変数を参照することです。 ↩︎