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
780
Other Decks in Technology
See All in Technology
宣言型部分更新は何を解決しどこへ向かうのか
shuta13
1
450
Code for Security プロジェクト紹介 2026年10月
yu23ki14
0
110
AI × アーキテクト 第1・2章|生成AIの課題とアーキテクトの素養
daidaidai
0
170
「事実」を「計測」して、「解釈」する 〜AWS Lambdaのメモリ最適解の見つけ方〜
maimyyym
0
170
Claude の新機能を、 出た日に触って社内に届けるまで
rfdnxbro
1
170
「Docker Desktop不要の時代」は 来たのか?
ueponx
0
770
大型犬好きによるPugの紹介
yamanoku
1
160
DevelopersIO 2026 知識の地図を描く ─ オントロジーとは何か?
ishikawa_satoru
1
1.7k
Classmethod Company Deck
satoshi7
1
680
Optunaの最新リリースv5.0について
pfn
PRO
0
120
Railsで挑む大量データ永続化 〜600億件を永続化するための設計とボトルネックとの戦い〜
occhi
1
580
Rustuna: Rustによる高速なOptuna実装
pfn
PRO
0
190
Featured
See All Featured
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
930
Building Adaptive Systems
keathley
44
3.2k
What Being in a Rock Band Can Teach Us About Real World SEO
427marketing
0
1.1k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
So, you think you're a good person
axbom
PRO
2
2.2k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
300
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Chasing Engaging Ingredients in Design
codingconduct
0
360
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
46k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
77k
The Invisible Side of Design
smashingmag
301
52k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
410
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で作られている部分