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
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
KPIだけでは評価できないプロダクトが考えるべき Evalsという第二の評価系 / Beyond KPIs: Evals as a Second Evaluation Framework for Products #PdEConf
aki_iinuma
4
3.8k
DEFCON34-Write-up_HYCu-MYCu
daikiokazaki
0
120
ペアプロの価値はコードを書くことだけじゃない
codmoninc
PRO
0
150
薬剤師(ドメインエキスパート)と一緒に育てる薬局向けAIアシスタント
kakehashi
PRO
2
130
Redmine 7.0で私が開発した新機能の狙いと背景
vividtone
1
130
AIで実装は速くなった。なのにプロダクトは速くならない。職能の壁を越えて価値のフローを設計する
nwiizo
7
7.6k
Amazon Quick on DesktopがIAM Identity Centerで動かない理由
yukiogawa
0
130
Multica × 長期記憶:40個のミニプロジェクト管理
eiei114
1
220
GoCon2026 - Open Source, Open World
sanposhiho
2
720
Tab5をRubyで動くパソコンにする
kishima
2
280
幾何アルゴリズムで なめらかなピン操作を / iOSDC Japan 2026 / smoothpin
kazumanagano
0
230
Microsoft 365 Copilot chat -tekoälypalvelun tietosuojaongelmat
hponka
0
570
Featured
See All Featured
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
470
BBQ
matthewcrist
89
10k
Leo the Paperboy
mayatellez
8
2.2k
Designing for Timeless Needs
cassininazir
1
470
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
370
Joys of Absence: A Defence of Solitary Play
codingconduct
1
490
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
Prompt Engineering for Job Search
mfonobong
0
450
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
660
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2.1k
Six Lessons from altMBA
skipperchong
29
4.5k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
2
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で作られている部分