강력 건너뛰기 모드

강력한 건너뛰기는 Compose 컴파일러에서 사용할 수 있는 모드입니다. 사용 설정하면 다음 두 가지 방법으로 컴파일러의 동작이 변경됩니다.

  • 매개변수가 불안정한 컴포저블은 건너뛸 수 있음이 됨
  • 불안정한 캡처가 있는 람다는 기억됩니다.

강력한 건너뛰기 모드 사용 설정

이전 출시에서 Gradle 모듈에 강력한 건너뛰기를 사용 설정하려면 Gradle 구성의 composeCompiler 블록에 다음 옵션을 포함하세요.

android { ... }

composeCompiler {
   enableStrongSkippingMode = true
}

컴포저블 건너뛰기 가능 여부

강한 건너뛰기 모드는 건너뛰기 및 컴포저블 함수와 관련하여 Compose 컴파일러에서 일반적으로 적용하는 일부 안정성 규칙을 완화합니다. 기본적으로 Compose 컴파일러는 모든 인수의 값이 안정적인 경우 구성 가능한 함수를 건너뛸 수 있는 것으로 표시합니다. 강력한 건너뛰기 모드로 인해 이러한 문제가 달라집니다.

강력한 건너뛰기가 사용 설정되면 모든 다시 시작 가능한 구성 가능한 함수를 건너뛸 수 있습니다. 이는 불안정한 매개변수가 있는지와 관계없이 적용됩니다. 다시 시작할 수 없는 구성 가능한 함수는 건너뛸 수 없습니다.

건너뛸 때

리컴포지션 중에 컴포저블을 건너뛸지 결정하기 위해 Compose는 각 매개변수의 값을 이전 값과 비교합니다. 비교 유형은 매개변수의 안정성에 따라 다릅니다.

  • 불안정한 매개변수는 인스턴스 동등성(===)을 사용하여 비교됩니다.
  • 안정적인 매개변수는 객체 동등성(Object.equals())을 사용하여 비교됩니다.

모든 매개변수가 이러한 요구사항을 충족하면 Compose는 리컴포지션 중에 컴포저블을 건너뜁니다.

컴포저블에서 강력한 건너뛰기를 선택 해제하는 것이 좋습니다. 즉, 다시 시작할 수는 있지만 건너뛸 수 없는 컴포저블을 사용해야 할 수 있습니다. 이 경우에는 @NonSkippableComposable 주석을 사용하세요.

@NonSkippableComposable
@Composable
fun MyNonSkippableComposable {}

클래스를 안정적인 것으로 주석 처리

인스턴스 등가 대신 객체 등가성을 사용하는 객체를 원한다면 지정된 클래스에 계속 @Stable 주석을 추가합니다. 전체 객체 목록을 관찰할 때 이를 실행해야 할 수 있습니다. Room과 같은 데이터 소스는 목록의 항목 중 하나가 변경될 때마다 목록의 모든 항목에 새 객체를 할당합니다.

람다 메모

강력한 건너뛰기 모드를 사용하면 컴포저블 내에서 람다의 메모이제이션도 더 많이 사용할 수 있습니다. 강력한 건너뛰기를 사용 설정하면 구성 가능한 함수 내의 모든 람다가 자동으로 기억됩니다.

강력한 건너뛰기를 사용할 때 컴포저블 내에서 람다 메모를 작성하기 위해 컴파일러는 remember 호출로 람다를 래핑합니다. 람다 캡처로 키가 지정됩니다.

다음 예와 같이 람다가 있는 경우를 생각해 보겠습니다.

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = {
        use(unstableObject)
        use(stableObject)
    }
}

강력한 건너뛰기를 사용 설정하면 컴파일러는 람다를 remember 호출로 래핑하여 람다를 기억합니다.

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = remember(unstableObject, stableObject) {
        {
            use(unstableObject)
            use(stableObject)
        }
    }
}

키는 구성 가능한 함수와 동일한 비교 규칙을 따릅니다. 런타임은 인스턴스 등식을 사용하여 불안정한 키를 비교합니다. 객체 동등성을 사용하여 안정적인 키를 비교합니다.

메모화 및 리컴포지션

이러한 최적화로 인해 리컴포지션 중에 런타임에서 건너뛰는 컴포저블의 수가 크게 증가합니다. 메모이제이션을 사용하지 않으면 런타임은 재구성 중에 람다 매개변수를 사용하는 모든 컴포저블에 새 람다를 할당할 가능성이 훨씬 높습니다. 결과적으로 새 람다에는 마지막 컴포지션과 같지 않은 매개변수가 있습니다. 이로 인해 재구성이 발생합니다.

메모이제이션 피하기

기억하고 싶지 않은 람다가 있다면 @DontMemoize 주석을 사용하세요.

val lambda = @DontMemoize {
    ...
}

APK 크기

건너뛸 수 있는 컴포저블은 컴파일 시 건너뛸 수 없는 컴포저블보다 더 많은 생성 코드를 생성합니다. 강력한 건너뛰기를 사용 설정하면 컴파일러는 거의 모든 컴포저블을 건너뛸 수 있는 것으로 표시하고 모든 람다를 remember{...}로 래핑합니다. 따라서 강력한 건너뛰기 모드를 사용 설정해도 애플리케이션의 APK 크기에 미치는 영향이 매우 적습니다.

Now In Android에서 강력 건너뛰기를 사용 설정하면 APK 크기가 4KB만큼 늘어났습니다. 크기 차이는 주로 이전에 특정 앱에 있었던 건너뛸 수 없는 컴포저블의 수에 따라 다르지만 상대적으로 작습니다.