安定性の問題を診断する

不要または過剰な再コンポーズによってパフォーマンスの問題が発生した場合は、アプリの安定性をデバッグする必要があります。このガイドでは、そのためのいくつかの方法について説明します。

Layout Inspector

Android Studio の Layout Inspector を使用すると、アプリ内で再コンポーズしているコンポーザブルを確認できます。Compose がコンポーネントを再コンポーズまたはスキップした回数が表示されます。

Layout Inspector での再コンポーズとスキップ回数

Compose コンパイラ レポート

Compose コンパイラは、安定性の推定の結果を検査用に出力できます。この出力を使用して、どのコンポーザブルがスキップ可能で、どのコンポーザブルがスキップ可能かを判断できます。以降のサブセクションでは、これらのレポートの使用方法について説明します。詳細については、技術ドキュメントをご覧ください。

セットアップ

コンパイラ コンパイラ レポートは、デフォルトでは有効になっていません。これらはコンパイラ フラグを使用して有効にできます。正確な設定はプロジェクトによって異なりますが、ほとんどのプロジェクトでは、次のスクリプトをルート build.gradle ファイルに貼り付けることができます。

Groovy

subprojects {
  tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
        kotlinOptions {
            if (project.findProperty("composeCompilerReports") == "true") {
                freeCompilerArgs += [
                        "-P",
                        "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
                                project.buildDir.absolutePath + "/compose_compiler"
                ]
            }
            if (project.findProperty("composeCompilerMetrics") == "true") {
                freeCompilerArgs += [
                        "-P",
                        "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
                                project.buildDir.absolutePath + "/compose_compiler"
                ]
            }
        }
    }
}

Kotlin

subprojects {
    tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
        kotlinOptions {
            if (project.findProperty("composeCompilerReports") == "true") {
                freeCompilerArgs += listOf(
                    "-P",
                    "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=${project.buildDir.absolutePath}/compose_compiler"
                )
            }
            if (project.findProperty("composeCompilerMetrics") == "true") {
                freeCompilerArgs += listOf(
                    "-P",
                    "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=${project.buildDir.absolutePath}/compose_compiler"
                )
            }
        }
    }
}

タスクを実行する

コンポーザブルの安定性をデバッグするには、次のようにタスクを実行します。

./gradlew assembleRelease -PcomposeCompilerReports=true

出力例

このタスクでは 3 つのファイルを出力します。JetSnack からの出力例を次に示します。

  • <modulename>-classes.txt: このモジュールのクラスの安定性に関するレポート。サンプル
  • <modulename>-composables.txt: モジュール内のコンポーザブルの再実行とスキップの可否に関するレポート。サンプル
  • <modulename>-composables.csv: スプレッドシートへのインポートまたはスクリプトを使用した処理が可能な、コンポーザブル レポートの CSV バージョン。サンプル

コンポーザブル レポート

composables.txt ファイルには、パラメータの安定性、再起動可能かスキップ可能かなど、特定のモジュールのコンポーズ可能な関数の詳細が含まれます。JetSnack の架空の例を次に示します。

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun SnackCollection(
  stable snackCollection: SnackCollection
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
  stable index: Int = @static 0
  stable highlight: Boolean = @static true
)

この SnackCollection コンポーザブルは完全に再起動可能で、スキップ可能で安定しています。必須ではありませんが、必須ではありません。

別の例を見てみましょう。

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  stable index: Int
  unstable snacks: List<Snack>
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
)

HighlightedSnacks コンポーザブルはスキップできません。Compose は再コンポーズ時にスキップしません。これは、どのパラメータも変更されていない場合でも発生します。これは、unstable パラメータの snacks が原因です。

クラスレポート

ファイル classes.txt には、指定されたモジュールのクラスに関する同様のレポートが含まれています。次のスニペットは、Snack クラスの出力です。

unstable class Snack {
  stable val id: Long
  stable val name: String
  stable val imageUrl: String
  stable val price: Long
  stable val tagline: String
  unstable val tags: Set<String>
  <runtime stability> = Unstable
}

参考までに、Snack の定義を次に示します。

data class Snack(
    val id: Long,
    val name: String,
    val imageUrl: String,
    val price: Long,
    val tagline: String = "",
    val tags: Set<String> = emptySet()
)

Compose コンパイラが Snack を不安定としてマークしました。これは、tags パラメータの型が Set<String> であるためです。これは、MutableSet でないため、不変の型です。ただし、Set, ListMap などの標準のコレクション クラスは、最終的にはインターフェースです。そのため、基になる実装はまだ変更可能です。

たとえば、val set: Set<String> = mutableSetOf("foo") と記述できます。変数は定数であり、宣言された型は可変ではありませんが、実装は引き続き可変です。Compose コンパイラは宣言された型しか参照しないため、このクラスの不変性を確認できません。したがって、tags が不安定になります。