Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Strong Skipping Mode によってrecompositionはどう変わったのか
Search
mikan
September 25, 2024
Technology
420
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Strong Skipping Mode によってrecompositionはどう変わったのか
DroidKaigi.onCompletion { 2024@Online }
https://yumemi.connpass.com/event/329691/
mikan
September 25, 2024
More Decks by mikan
See All by mikan
Loop Engineering これくらいでいい
mikanichinose
0
250
形式手法を使って仕様をコーディングしよう
mikanichinose
0
200
Navigation3でやりとりした画面引数をSavedStateHandleから引き出せない理由
mikanichinose
0
40
Lazy APIを使ってGradleビルド速度を改善する
mikanichinose
1
94
Navigation3でViewModelにデータを渡す方法
mikanichinose
0
770
「脳に収まるコードの書き方」を読んで学んだこと
mikanichinose
1
240
RepositoryのSSoT化
mikanichinose
0
110
Kotlin Multiplatform 始めました
mikanichinose
1
170
Web APIをなぜつくるのか
mikanichinose
0
4.4k
Other Decks in Technology
See All in Technology
初海外で注意すべきこと12選!!!安全快適なre:invent生活にするために
yuuuuuuu168
1
160
Tofu-Controller lightning talk @ Containerday 2026
gionn
0
110
DGX Spark でどこまでハッキングができるのか?
kash1064
3
1.3k
Claude Code本は、どのように執筆チームで書かれたか
oikon48
1
380
コンテナ運用におけるネットワーク・コンテナコストの削減方法/How to Reduce Network and Container Costs in Container Operations
masakiokuda
1
150
会社説明資料
persolserverworks
0
9.8k
JAWSUG TOHOKU 青森支部 「LoGoチャット」のAWS移行で 乗り越えたこと
taketakekaho
0
330
サブエージェントと Claude Mods で作る独自 Advisor ツールの話
kenichiodo
2
160
無理せず続ける、子育てエンジニアの学び
codmoninc
PRO
0
860
Claude の新機能を、 出た日に触って社内に届けるまで
rfdnxbro
1
170
Post AI時代のリファクタリング実践記 / Refactoring in the post AI era
kaminashi
1
230
AIにシークレットを見せない、WSL2で安全な開発環境
yazm
0
300
Featured
See All Featured
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6.1k
Ethics towards AI in product and experience design
skipperchong
2
430
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
2
2.2k
Information Architects: The Missing Link in Design Systems
soysaucechin
1
1.2k
Marketing to machines
jonoalderson
1
5.9k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
550
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
640
Fantastic passwords and where to find them - at NoRuKo
philnash
53
4k
SEO for Brand Visibility & Recognition
aleyda
0
4.8k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.8k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.7k
Between Models and Reality
mayunak
4
500
Transcript
Strong Skipping Mode によって recompositionはどう変わったの か DroidKaigi.onCompletion { 2024@Online }
mikan(一瀬喜弘)
自己紹介
目的 Strong Skipping Mode を有効にすることで、不安定なパラメ ータに依存するComposable関数のrecompositionに、どのよ うな違いが生じるのか検証してみた 注意 コード例は検証用のものなので、およそプロダクションコー ドで利用するようなものにはなってません
Strong Skipping Mode とは TL;DR 不安定なパラメータに依存しているComposable関数も skippableがつくようになった
Strong Skipping Mode を有効にする 方法はいろいろある 1. compose-runtime:1.7.0 2. kotlin 2.0.20
3. compose compiler にオプションを渡す // build.gradle.kts // 1. tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>() { compilerOptions.freeCompilerArgs.addAll( "-P", "plugin:androidx.compose.compiler.plugins.kotlin:experimentalStrongSkipping=true", ) } composeCompiler { // 2. before kotlin 2.0.20 enableStrongSkippingMode = true // 3. after kotlin 2.0.20 featureFlags = setOf( ComposeFeatureFlag.StrongSkipping.disabled() // 無効にする書き方
検証用コード @Composable fun Names( names: List<String>, ) { Row( horizontalArrangement
= Arrangement.spacedBy(8.dp), ) { names.forEach { Text(it) } } }
検証用コード class UnstableUserClass( val names: List<String>, ) @Composable fun User(user:
UnstableUserClass) { Names(user.names) }
検証用コード data class UnstableUserDataClass( val names: List<String>, ) @Composable fun
User(user: UnstableUserDataClass) { Names(user.names) }
検証用コード @Stable class StableUserClass( val names: List<String>, ) @Composable fun
User(user: StableUserClass) { Names(user.names) }
検証用コード @Immutable class ImmutableUserClass( val names: List<String>, ) @Composable fun
User(user: ImmutableUserClass) { Names(user.names) }
検証用コード @Stable data class StableUserDataClass( val names: List<String>, ) @Composable
fun User(user: StableUserDataClass) { Names(user.names) }
検証用コード @Immutable data class ImmutableUserDataClass( val names: List<String>, ) @Composable
fun User(user: ImmutableUserDataClass) { Names(user.names) }
@Composable fun Users( unstableUserClass: UnstableUserClass, unstableUserDataClass: UnstableUserDataClass, stableUserClass: StableUserClass, immutableUserClass:
ImmutableUserClass, stableUserDataClass: StableUserDataClass, immutableUserDataClass: ImmutableUserDataClass, names: List<String>, modifier: Modifier = Modifier, ) { Column(modifier) { User(unstableUserClass) User(unstableUserDataClass) User(stableUserClass) User(immutableUserClass) User(stableUserDataClass) User(immutableUserDataClass) Names(names) } }
@Composable fun MainScreen1( count: Int, onChangeCount: (Int) -> Unit, modifier:
Modifier = Modifier, ) { val names = mutableListOf("mikan") val unstableUserClass = UnstableUserClass(names) val unstableUserDataClass = UnstableUserDataClass(names) val stableUserClass = StableUserClass(names) val immutableUserClass = ImmutableUserClass(names) val stableUserDataClass = StableUserDataClass(names) val immutableUserDataClass = ImmutableUserDataClass(names) Column(modifier) { Text("Count: $count") Button({ names += "mikan" onChangeCount(count + 1) }) { Text("Increment") } Users( unstableUserClass, unstableUserDataClass, stableUserClass, immutableUserClass, stableUserDataClass, immutableUserDataClass, names, ) } }
@Composable fun MainScreen2( count: Int, onChangeCount: (Int) -> Unit, modifier:
Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // positional memoized by remember val unstableUserClass = UnstableUserClass(names) val unstableUserDataClass = UnstableUserDataClass(names) val stableUserClass = StableUserClass(names) val immutableUserClass = ImmutableUserClass(names) val stableUserDataClass = StableUserDataClass(names) val immutableUserDataClass = ImmutableUserDataClass(names) Column(modifier) { Text("Count: $count") Button({ names += "mikan" onChangeCount(count + 1) }) { Text("Increment") } Users( unstableUserClass, unstableUserDataClass, stableUserClass, immutableUserClass, stableUserDataClass, immutableUserDataClass, names, ) } }
@Composable fun MainScreen3( count: Int, onChangeCount: (Int) -> Unit, modifier:
Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } val unstableUserClass = remember { UnstableUserClass(names) } val unstableUserDataClass = remember { UnstableUserDataClass(names) } val stableUserClass = remember { StableUserClass(names) } val immutableUserClass = remember { ImmutableUserClass(names) } val stableUserDataClass = remember { StableUserDataClass(names) } val immutableUserDataClass = remember { ImmutableUserDataClass(names) } Column(modifier) { Text("Count: $count") Button({ names += "mikan" onChangeCount(count + 1) }) { Text("Increment") } Users( unstableUserClass, unstableUserDataClass, stableUserClass, immutableUserClass, stableUserDataClass, immutableUserDataClass, names, ) } }
MainScreen1 すべてrecompositionした。recompositionの前後で見た目は変わらず → recompositionの度にすべての変数が再割り当てされているから → StableUserDataClassとImmutableUserDataClassがrecompositionした理由について、この説明だと違和感 が残る
MainScreen2 (mutableListにrememberをつけたやつ) @Stable と @Immutable を つけた data class だけスキップした。
recompositionした ものは 描画が 更新された
MainScreen2 (mutableListにrememberをつけたやつ) @Composable fun MainScreen2( count: Int, onChangeCount: (Int) ->
Unit, modifier: Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // recomposition時: キャッシュが存在するので再割り当ては発生しない val unstableUserClass = UnstableUserClass(names) // UnstableUserClass(["mikan", "mikan"]) → UnstableUserClass(["mikan", "mikan"]) val unstableUserDataClass = UnstableUserDataClass(names) // UnstableUserDataClass(["mikan", "mikan"]) → UnstableUserDataClass(["mikan", "mikan"]) val stableUserClass = StableUserClass(names) // StableUserClass(["mikan", "mikan"]) → StableUserClass(["mikan", "mikan"]) val immutableUserClass = ImmutableUserClass(names) // ImmutableUserClass(["mikan", "mikan"]) → ImmutableUserClass(["mikan", "mikan"]) val stableUserDataClass = StableUserDataClass(names) // StableUserDataClass(["mikan", "mikan"]) → StableUserDataClass(["mikan", "mikan"])
MainScreen2 (mutableListにrememberをつけたやつ) @Composable fun MainScreen2( count: Int, onChangeCount: (Int) ->
Unit, modifier: Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // recomposition時: キャッシュが存在するので再割り当ては発生しない val unstableUserClass = UnstableUserClass(names) // skippableでないのでrecompositionする val unstableUserDataClass = UnstableUserDataClass(names) // skippableでないのでrecompositionする val stableUserClass = StableUserClass(names) // skippableだが、再割り当てによって参照が変わっているのでrecompositionする val immutableUserClass = ImmutableUserClass(names) // skippableだが、再割り当てによって参照が変わっているのでrecompositionする val stableUserDataClass = StableUserDataClass(names) // skippableであり、equals比較において同じとみなされるのでrecompositionしない
MainScreen3 (変数すべてにrememberをつけたやつ) @Stable と @Immutable をつけたものについてはスキップした。recompositionしたものは描画が更新された
MainScreen3 (変数すべてにrememberをつけたやつ) @Composable fun MainScreen3( count: Int, onChangeCount: (Int) ->
Unit, modifier: Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // recomposition時: キャッシュされているため再割り当ては発生しない val unstableUserClass = remember { UnstableUserClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない val unstableUserDataClass = remember { UnstableUserDataClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない val stableUserClass = remember { StableUserClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない val immutableUserClass = remember { ImmutableUserClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない val stableUserDataClass = remember { StableUserDataClass(names) } // recomposition時: キャッシュされているため再割り当ては発生しない
MainScreen3 (変数すべてにrememberをつけたやつ) @Composable fun MainScreen3( count: Int, onChangeCount: (Int) ->
Unit, modifier: Modifier = Modifier, ) { val names = remember { mutableListOf("mikan") } // recomposition時: キャッシュされているため再割り当ては発生しない val unstableUserClass = remember { UnstableUserClass(names) } // skippableでないのでrecompositionする val unstableUserDataClass = remember { UnstableUserDataClass(names) } // skippableでないのでrecompositionする val stableUserClass = remember { StableUserClass(names) } // skippableであり、再割り当ては発生していないのでスキップ val immutableUserClass = remember { ImmutableUserClass(names) } // skippableであり、再割り当ては発生していないのでスキップ val stableUserDataClass = remember { StableUserDataClass(names) } // skippableであり、再割り当ては発生していないのでスキップ
Strong Skipping Mode MainScreen1 すべてrecompositionした: 変化なし
Strong Skipping Mode MainScreen2 (mutableListにrememberをつけたやつ) @Stable と @Immutable をつけた data
class および listを単純に渡しているものについてはスキップした → listはキャッシュが使われるのでスキップしたと考えられる
Strong Skipping Mode MainScreen3 (変数すべてにrememberをつけたやつ) すべてスキップした → すべてskippableになり、再割り当ては発生していないのでスキップしたものと考えられる
Strong Skipping Mode 気になった点 @Composable fun Names( names: List<String>, )
{ Row( horizontalArrangement = Arrangement.spacedBy(8.dp), ) { names.forEach { Text(it) } } } Names(listOf("mikan")) // skip
まとめ Strong Skipping Mode を有効化することで、不安定な型であっても同値であればrecompositionをスキップ するようになった 公式ドキュメントには、不安定な型については === で比較するとあったが、 ==
で比較しているように 見える これまでたまたま描画が更新できていた箇所が、Strong Skipping Mode を有効化によって更新しなくなる 可能性がある
ご清聴ありがとうございました