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
Jetpack Composeのパフォーマンスの基本
Search
松田
June 30, 2022
Technology
710
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Jetpack Composeのパフォーマンスの基本
以下で話したJetpack Composeのパフォーマンスの基本についてのスライドです
https://andpad.connpass.com/event/249842/
松田
June 30, 2022
More Decks by 松田
See All by 松田
GraphQL導入への技術選定
matsudamper
2
760
Other Decks in Technology
See All in Technology
`t*(42&t>>10)`だけで音楽が鳴る、Swiftで実装するBytebeat / iOSDC Japan 2026
yutailang0119
0
120
JSONataとAWS Step Functionsで目指すRuntimelessな世界
mu7889yoon
0
360
aws-iot-platform-architecture-use-cases.pdf
ma2shita
0
440
AI de Idea
kawaguti
PRO
2
130
AIによるクリエイティブ生成を行う上での試行錯誤
plaidtech
PRO
0
160
株式会社シーエーシー エンジニア向け会社紹介資料
cac
0
57k
アクセスキーこわい やめかたと漏らさない工夫
sassssan68
1
550
すぐできる衛星通信対応 あとは山奥に行くだけ
tatetate55
0
140
その Lambda、8分で 管理者権限まで奪われます
k1nakayama
7
3.4k
LLMに渡さなかった仕事
nanaism
0
1.3k
「どのログを見ればいい?」 から始めた サーバーレス障害解析
y_waka1
1
110
エージェントはローカル、検証はMicroVM — Lambda MicroVMsでつくるServerless CI
fujioka6789
3
600
Featured
See All Featured
Mind Mapping
helmedeiros
1
360
GitHub's CSS Performance
jonrohan
1033
470k
Un-Boring Meetings
codingconduct
0
420
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
600
Done Done
chrislema
186
17k
Testing 201, or: Great Expectations
jmmastey
46
8.3k
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
69
65k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.7k
HDC tutorial
michielstock
2
880
Embracing the Ebb and Flow
colly
88
5.2k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
30 Presentation Tips
portentint
PRO
1
400
Transcript
Jetpack Composeの パフォーマンスの基本 Retty アプリチーム 松田 2022/06/30
自己紹介 Retty株式会社 アプリチーム所属 松田 • 2020年入社 3年目 • やっていること
◦ 主にAndroid ◦ たまにiOS ◦ バックエンドサーバー(Kotlin) アプリエンジニア 特にAndroid / Kotlinバックエンドサーバー に興味があるエンジニア募集中
Rettyとは
1.Jetpack Composeの基本の動き方 https://developer.android.com/jetpack/compose/phases 例外) ・BoxWithConstraints ・LazyColumn ・LazyRow 単方向データフローで動 いている
1.Jetpack Composeの基本の動き方 https://developer.android.com/jetpack/compose/phases Treeの作成
1.Jetpack Composeの基本の動き方 https://developer.android.com/jetpack/compose/phases ・サイズ計算 ・座標計算
1.Jetpack Composeの基本の動き方 https://developer.android.com/jetpack/compose/phases いかに効率化するか いかにReCompositionを行わずに Layoutフェーズだけ実行できるか
目次 1. Jetpack Composeの動作フェーズ 2. Composable 2.1. Compositionをskipする 2.2. Rettyで使用している設計例
2.3. Restart不可能なComposable 3. Layout 3.1. Layoutフェーズだけを実行する
2.StableとSkippable @Composable public fun MainContent( title: String, color: Color, items:
List<String>, ) { // ~~~~ } 引数がequalsで比較して同一 であればそのComposable内 のCompositionはスキップされ る ※Composable関数がStable である場合に限る
2.StableとSkippable val mutableItems = mutableListOf<String>() val items: List<String> = mutableItems
mutableItems.add("1") println(items) // [1] 1. Listは外部から変更可能。 → これがStableではないという事 2. 外部から変更された事をComposeは検知できない。 3. なのでskipされずに毎回Compositionが行われる。
2.Stableか調べる allprojects { gradle.projectsEvaluated { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { kotlinOptions { def
baseDir = "path/to/dir" def outPath = "${baseDir}${project.path.replace(":", "/")} " freeCompilerArgs += [ "-P", "plugin:androidx.compose.compiler.plugins.kotlin :metricsDestination= ${outPath}", "-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination= ${outPath}" ] } } } } https://github.com/androidx/androidx/blob/403b6d0032c289ed6e65a1c75f137c07aedd220f/compose/compiler/design/compiler-metrics.md TopLevelの build.gradle
2.Stableか調べる 1. モジュールごとのSummary(*-module.json) 2. Composable関数の情報(*-composables.txt) 3. Composable関数の引数に与えるクラスの情報(*-classes.txt)
2.Stableか調べる { "skippableComposables": 81, "restartableComposables": 86, "readonlyComposables": 0, "totalComposables":
86, "restartGroups": 86, "totalGroups": 129, "staticArguments": 129, "certainArguments": 52, "knownStableArguments": 961, "knownUnstableArguments": 20, "unknownStableArguments": 10, "totalArguments": 991, "markedStableClasses": 0, "inferredStableClasses": 16, "inferredUnstableClasses": 4, "inferredUncertainClasses": 1, "effectivelyStableClasses": 16, "totalClasses": 21, "memoizedLambdas": 68, "singletonLambdas": 32, "singletonComposableLambdas": 3, "composableLambdas": 12, "totalLambdas": 71 } { "skippableComposables": 81, "restartableComposables": 86, "totalComposables": 86 } *-module.json 5つはSkip可能でない
2.Stableか調べる *-composables.txt restartable fun ListItem( stable modifier: Modifier
unstable uiState: ListItemUiState ) • 引数が全てstable • skippableである restartable skippable fun Label( stable drawableRes: Int stable colorRes: Int stable contentDescription: String ) • 引数にunstableがある • skippableでない
2.Stableか調べる *-classes.txt unstable class ListItemUiState{ stable val iconPath: String?
stable val userName: String unstable val labels: List<String> stable val isDraft: Boolean stable val onClick: Function0<Unit> <runtime stability> = Unstable } • unstableな値があるとクラスも Unstableになる • Lambdaはstable
2.Stableにする UnstableをStableにしてskippableにするにはどうしたら良いかの前に 1. Composable関数がskippableで無いのは、必ずしも悪い事では無いと記述 がある https://github.com/androidx/androidx/blob/403b6d0032c289ed6e65a1c75f137c07aedd220f/compose /compiler/design/compiler-metrics.md 2. 公式ドキュメントのサンプルにも普通にunstableを引数に取っている
Rettyでは実装が難しく無い限り基本的に StableにしてSkippableにする
2.Stableにする @StableMarker 1. @Immutable ◦ publicな値は変更されない ◦ MutableStateを通しても値が変更されない 2.
@Stable ◦ publicな値は変更されない ◦ MutableStateを通して値が変更される https://developer.android.com/reference/kotlin/androidx/compose/runtime/StableMarker
2.Stableにする ListをStableにする @Immutable public class ImmutableList<E>(list: List<E>) : List<E>
by list.toList()
Rettyで使用している設計例
2.Rettyで使用している設計例 Root @Immutable public data class NamesScreenUiState( public val names:
ImmutableList<NameUiState> , ) @Immutable public data class NameUiState( public val name: String, public val onClick: ComposeEventWrapper <() -> Unit>, } Listの要素 比較時に必ずtrue になるラッパー
2.Rettyで使用している設計例 @Composable public fun NamesScreen ( modifier: Modifier , uiState:
NamesScreenUiState , ) { Column(modifier = modifier.fillMaxWidth()) { LazyColumn(modifier = Modifier.fillMaxWidth()) { items(uiState.names) { item -> Name( modifier = Modifier.fillMaxWidth(), uiState = item ) } } } } @Composable public fun Name( modifier: Modifier, uiState: NameUiState, ) { Column( modifier = modifier.clickable { uiState.onClick.event() } ) { Text(text = uiState.name) } }
Restart不可能なComposable
2.Restart不可能なComposable { "skippableComposables": 81, "restartableComposables": 86, "readonlyComposables": 0,
"totalComposables": 86, "restartGroups": 86, "totalGroups": 129, "staticArguments": 129, "certainArguments": 52, "knownStableArguments": 961, "knownUnstableArguments": 20, "unknownStableArguments": 10, "totalArguments": 991, "markedStableClasses": 0, "inferredStableClasses": 16, "inferredUnstableClasses": 4, "inferredUncertainClasses": 1, "effectivelyStableClasses": 16, "totalClasses": 21, "memoizedLambdas": 68, "singletonLambdas": 32, "singletonComposableLambdas": 3, "composableLambdas": 12, "totalLambdas": 71 } { "skippableComposables": 81, "restartableComposables": 86, "totalComposables": 86 } *-module.json
2.@NonRestartableComposable @Composable public fun Restartable(count: Int) { SideEffect { Log.d("Restartable",
"SideEffect") } Text(text = "Restartable: ButtonCount=$count") } @Composable @NonRestartableComposable public fun NonRestartable(count: Int) { SideEffect { Log.d("NonRestartable", "SideEffect") } Text(text = "NonRestartable: ButtonCount=$count") } 1 2 2
2.@NonRestartableComposable var nonRestartable by remember { mutableStateOf(0) } var restartable
by remember { mutableStateOf(0) } Column { Button(onClick = { restartable++ }) { Text(text = "Restartable") } Restartable(restartable) Button(onClick = { nonRestartable++ }) { Text(text = "NonRestartable") } NonRestartable(nonRestartable) } Restartable D SideEffect NonRestartable D SideEffect ログ出力
2.@NonRestartableComposable Compose 1.1 での使用箇所
2.@NonRestartableComposable Compose 1.2.0 (beta2) での使用箇所
2.@NonRestartableComposable リスタート不可能にすると良い時 • 再コンポジションの「ルート」になることがないと 考えられる場合 • つまり、Composable関数が直接変数を読み込まない場 合 全て別のComposableに渡しているので、 Card側は再起動可能にする必要はない
Layoutフェーズだけを実行する
3.Layoutフェーズだけを実行する スクロールしても画像の位置は 変わっていないように見える CollapsingLayout
3.Layoutフェーズだけを実行する @Composable public fun CollapsingLayout ( modifier: Modifier =
Modifier , scrollOffset: Int , content: @Composable () -> Unit ) { Layout( modifier = modifier, content = content ) { measurables, constraints -> TODO("scrollOffsetを使って計算") } } 1 2 Compositionの回数 ※ComposeRuntime1.2から使える LayoutInspectorの機能
3.Layoutフェーズだけを実行する @Composable public fun CollapsingLayout ( modifier: Modifier =
Modifier , scrollOffsetProvider: () -> Int, content: @Composable () -> Unit ) { Layout( modifier = modifier, content = content ) { measurables, constraints -> val scrollOffset = scrollOffsetProvider() TODO("scrollOffsetを使って計算") } } 1 2 ※ComposeRuntime1.2から使える LayoutInspectorの機能 Compositionの回数
3.Layoutフェーズだけを実行する 公式の例では、offsetが、Lambda版と非Lambda版がある。 • 固定サイズ -> 非Lambda版 • アニメーションする ->
Lambda版
最後に: Composeで作られている部分