Slide 1

Slide 1 text

Google I/O 2022 社内LT会 Jetpack Compose のパフォーマンス 2022/06/24 赤城 史哉

Slide 2

Slide 2 text

話すこと - パフォーマンス計測の前に - Compose の段階 - 落とし穴 1 : LazyList Key - 落とし穴 2 : derivedStateOf - 落とし穴 3 : Modifier.drawBhind

Slide 3

Slide 3 text

パフォーマンス計測の前に - リリースモードでビルドし、 R8 を使用する - デバッグの体験を向上させる、デバッグモードではパフォーマンスの最適化が行われない。 - R8 で冗長なコードが削除される - Layout inspector の recomposition count が便利 - 動作環境 - Android Studio Electric Eel - Jetpack compose version : 1.2.0-alpha03 or higher.

Slide 4

Slide 4 text

Layout inspector の recomposition count Recompositionを スキップした回数 Recompose回数

Slide 5

Slide 5 text

Composeの段階 - Composition / コンポジション - UI の構成を決める。ツリーを作成。 - Layout / レイアウト - UI の配置、大きさを決める。 - Drawing / 描画 - UI 要素をキャンバス(通常はデバイス画面)に描画

Slide 6

Slide 6 text

LazyList Key // Do not @Composable fun NotesList(notes: List) { LazyColumn { items( items = notes ) { note -> NoteRow(note) } } } Item 1 Item 2 Item 3 Item 4 Item 5 Item 2 Item 3 Item 4 Item 5 - LazyColumn の item のある要素が削除されると、以下の Item 全てが Recompose され てしまう

Slide 7

Slide 7 text

LazyList Key Item 1 Item 2 Item 3 Item 4 Item 5 Item 2 Item 3 Item 4 Item 5 - LazyColumn に Key を設定すれば、 item の index が変わっても適切に recomposition が制御されます。 // Do @Composable fun NotesList(notes: List) { LazyColumn { items( items = notes, key = { note -> // Return a stable, // unique key for the note note.id } ) { note -> NoteRow(note) } } }

Slide 8

Slide 8 text

LazyList Key - Key が重複すると、 Exception が吐かれるので注意

Slide 9

Slide 9 text

derivedStateOf // Do not val listState = rememberLazyListState() LazyColumn(state = listState) { // ... } val showButton = listState.firstVisibleItemIndex > 0 AnimatedVisibility(visible = showButton) { ScrollToTopButton() } Scrollをする度に、Scrollしている最中の全ての フレームでRecomposeされる

Slide 10

Slide 10 text

derivedStateOf - derivedStateOf で listState など高い頻度で更新される state を wrap すると、 必要な時のみ recomposition されるように制御出来る。 // Do val listState = rememberLazyListState() LazyColumn(state = listState) { // ... } val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } AnimatedVisibility(visible = showButton) { ScrollToTopButton() } この条件式の結果が変わったタイミングでのみ Recomposeされる

Slide 11

Slide 11 text

Modifier.drawBhind を使って不要な Stepをスキップ - animateColorBetween で color が変わるたびに Recompose されてしまう // Here, assume animateColorBetween() is a function that swaps between // two colors val color by animateColorBetween(Color.Cyan, Color.Magenta) Box(Modifier.fillMaxSize().background(color)) 2色の間でアニメーションされる架空の関数 毎フレームcolorが更新される

Slide 12

Slide 12 text

Modifier.drawBhind を使って不要な Stepをスキップ - drawBehind を使うと、 color に変更があっても composition と layout はスキップされ、 draw だけ行われる。 - layout inspector では draw の実行のみだと recomposition として扱われなかった。 val color by animateColorBetween(Color.Cyan, Color.Magenta) Box( Modifier .fillMaxSize() .drawBehind { drawRect(color) } )

Slide 13

Slide 13 text

参考資料 - Compose のパフォーマンス - https://developer.android.google.cn/jetpack/compose/performance?hl=ja - Jetpack Compose の一般的なパフォーマンスの落とし穴 / Google IO 2022 - https://io.google/2022/program/213421b6-9873-464f-9b36-38eeb232a854/intl/ja/ - Compose tooling / Layout Inspector - https://developer.android.com/jetpack/compose/tooling#layout-inspector