안정성 문제 진단

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

Layout Inspector

Android 스튜디오의 Layout Inspector를 사용하면 앱에서 재구성되는 컴포저블을 확인할 수 있습니다. Compose가 구성요소를 재구성하거나 건너뛴 횟수가 표시됩니다.

Layout Inspector의 리컴포지션 및 건너뛰기 횟수

Compose 컴파일러 보고서

Compose 컴파일러는 검사를 위해 안정성 추론 결과를 출력할 수 있습니다. 이 출력을 사용하여 건너뛸 수 있는 컴포저블과 건너뛸 수 없는 컴포저블을 확인할 수 있습니다. 다음 하위 섹션에서는 이러한 보고서를 사용하는 방법을 요약하지만 자세한 내용은 기술 문서를 참고하세요.

설정

Compose 컴파일러 보고서는 기본적으로 사용 설정되지 않습니다. 컴파일러 플래그를 사용하여 활성화할 수 있습니다. 정확한 설정은 프로젝트에 따라 다르지만 Compose 컴파일러 Gradle 플러그인을 사용하는 프로젝트의 경우 각 모듈의 build.gradle 파일에 다음을 추가할 수 있습니다.

  android { ... }

  composeCompiler {
    reportsDestination = layout.buildDirectory.dir("compose_compiler")
    metricsDestination = layout.buildDirectory.dir("compose_compiler")
  }

이제 프로젝트를 빌드할 때 Compose 컴파일러 보고서가 생성됩니다.

출력 예

reportsDestination는 세 개의 파일을 출력합니다. 다음은 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의 모든 매개변수가 stable로 표시되므로 Compose는 구조적 동등성 (Object.equals())을 사용하여 각 매개변수를 비교합니다. stable 매개변수는 프리미티브 값과 저렴한 .equals() 검사가 있는 작은 플랫 데이터 클래스에 적합하지만 컬렉션 보유 모델 (예: List<Snack>를 래핑하는 SnackCollection)을 stable로 강제하면 Compose는 리컴포지션마다 각 항목에서 .equals()를 호출합니다. 강력한 건너뛰기 모드를 사용 설정하면 컬렉션 unstable를 보유하는 모델을 유지하는 것이 좋습니다. Compose는 대신 인스턴스 동등성 (===)을 사용하여 모델을 비교합니다.

또는 강력한 건너뛰기 모드를 사용 중지한 상태에서 생성된 다른 예를 살펴보겠습니다.

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
)

강력한 건너뛰기 모드가 사용 중지된 경우 (Kotlin 2.0.20 이전) HighlightedSnacks 컴포저블은 건너뛸 수 없습니다. unstable 매개변수 snacks로 인해 매개변수가 변경되지 않은 경우에도 Compose는 리컴포지션 중에 이를 건너뛰지 않습니다.

강력한 건너뛰기 모드를 사용 설정하면 (Kotlin 2.0.20 이상의 기본값) HighlightedSnacks가 skippable로 표시되고 Compose는 인스턴스 동등성 (===)을 사용하여 unstable 매개변수 snacks를 비교합니다. 결과적으로 Compose는 동일한 List 참조가 전달되면 HighlightedSnacks를 건너뛰고 새로 할당된 List 인스턴스가 전달되는 경우에만 재구성합니다.

수업 보고서

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, List, Map와 같은 표준 컬렉션 클래스는 궁극적으로 인터페이스입니다. 따라서 기본 구현은 여전히 변경 가능할 수 있습니다.

예를 들어 val set: Set<String> = mutableSetOf("foo")를 작성할 수 있습니다. 변수는 상수이고 선언된 유형은 변경할 수 없지만 구현은 여전히 변경할 수 있습니다. Compose 컴파일러는 선언된 유형만 표시하므로 이 클래스의 불변성을 확신할 수 없습니다. 따라서 tags를 불안정으로 표시합니다.