안정성 문제 진단

불필요하거나 과도한 재구성으로 인해 성능 문제가 발생하면 앱의 안정성을 디버그해야 합니다. 이 가이드에서는 이를 위한 몇 가지 방법을 설명합니다.

Layout Inspector

Android 스튜디오의 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

출력 예

이 작업은 세 개의 파일을 출력합니다. 다음은 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가 불안정하다고 표시됩니다.