Compose를 개발하며 항상 stable한 구조를 유지하려고 생각하고있는데 사이드 프로젝트를 개발 하던 중 한번 점검을 하려 컴파일러 리포트를 뽑아보았다.
compose를 사용한 모듈의 build.gradle에 해당 옵션을 집어넣으면 다음 빌드 때 자동으로 리포트가 생성된다.
android {
composeCompiler {
reportsDestination = layout.buildDirectory.dir("compose_compiler")
metricsDestination = layout.buildDirectory.dir("compose_compiler")
}
}
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HomeView(
unstable requestDate: LocalDate?
stable navigate: Function1<Screens, Unit>
unstable viewModel: HomeViewModel? = @dynamic rememberBaseViewModel($composer, 0)
stable snackbarHostState: SnackbarHostState? = @dynamic LocalSnackbarHost.<get-current>($composer, 0b0110)
stable dialogDataHolder: DialogDataHolder? = @dynamic LocalDialogDataHolder.<get-current>($composer, 0b0110)
)
unstable한 파라미터가 있는 경우 컴포즈 컴파일러는 해당 컴포저블을 unskippable로 마킹하는데 왜 리포트는 skippable인지 찾아보던 도중
예전에 코틀린 2.0.0으로 마이그레이션을 준비하면서 봤던 Strong Skipping Mode를 찾게되었다.
공식문서의 일부분을 보면
Strong skipping mode relaxes some of the stability rules normally applied by the Compose compiler when it comes to skipping and composable functions. By default, the Compose compiler marks a composable function as skippable if all of its arguments have stable values. Strong skipping mode changes this.
With strong skipping enabled, all restartable composable functions become skippable. This applies whether or not they have unstable parameters. Non-restartable composable functions remain unskippable.
unstable한 파라미터의 존재 여부와는 상관없이 restartable한 컴포저블은 모두 skippable로 마킹한다는 내용이다. 그럼 어떻게 스킵 여부를 판단하는걸까?
unstable한 파라미터는 === 연산으로 메모리 주소가 같은 인스턴스인지 검사하고
stable 파라미터는 Object.equals()를 통해 같은 내용을 가지고 있는지 비교해 스킵 여부를 결정한다.
컴포저블이지만 Strong skipping의 영향을 받고싶지않다면, 매번 반드시 리컴포지션이 되어야하나면@NonSkippableComposable 어노테이션을 통해서 skippable 마크를 받지 않도록 제외할 수도 있다.
코틀린 2.0.20 부터는 String skipping mode가 기본적으로 사용하게 되었지만 가능한 stable한 컴포저블을 구현하는 방식이 Compose 개발에 있어 더 알맞는 방법이라고 생각한다.
다만 이제는 stable을 지키는데 있어 너무 오버헤드가 큰 경우나 몇몇 이유가 있을 때 의도하는 동작을 하는지에 대한 테스트가 잘 이루어지면 선택할 수 있는 옵션이 하나 늘어났다고 생각한다.