👇🏻 참고자료
컴포저블 수명 주기 | Jetpack Compose | Android Developers
컴포저블 수명 주기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 이 페이지에서는 컴포저블의 수명 주기에 관해 알아보며 Compose에서 컴포저블에 재구성
developer.android.com
최근 대부분의 토이 프로젝트에서 Compose를 이용해 UI를 만들고 있기 때문에,
Compose의 성능에 관해서도 관심을 가지게 되었습니다. 🤗
Compose UI 성능에서 중요한 부분 중에 하나가 바로 Recomposition이 발생하는 횟수가 아닐까 하는데요.
이번 포스트에서는 Recomposition이 발생하는 경우에 대해 알아보고
직접 여러 경우의 수를 코드로 확인해보면서 정리해보고자 합니다.🔥
안드로이드 독학 중인 초보자이기 때문에
만약 포스트에 잘못된 점이 있다면 꼭 댓글로 말씀 부탁드립니다. 😌
1. Recomposition을 skip하는 경우
🌹 안드로이드 공식문서를 보면 Composable이 이미 Composition tree에 있고, 모든 input이 stable 타입이며 변경되지 않았다면 해당 Composable은 Recomposition을 skip할 수 있다고 합니다.
이를 바탕으로 Recomposition이 일어나는 경우에 대해서 정리해보았습니다.
2. Recomposition이 발생하는 경우는?
👉🏻 참고 코드
@Composable
fun Parent() {
val state = remember { mutableStateOf(0) }
println("parent recomposition")
Column {
Child1(state = state)
Child2(state = state)
Button(onClick = { state.value++ }) {
Text(text = "Up state")
}
}
}
@Composable
fun Child1(state: MutableState<Int>) {
println("child 1 recomposition")
Leaf1(state = state)
}
@Composable
fun Leaf1(state: MutableState<Int>) {
println("Leaf1 recomposition")
Text(text = "${state.value}")
}
@Composable
fun Child2(state: MutableState<Int>) {
println("child 2 recomposition")
Text(text = "Child 2")
Leaf2(state = state)
}
@Composable
fun Leaf2(state: MutableState<Int>) {
println("Leaf2 recomposition")
Text(text = "Leaf 2 ${state.value}")
}
2.1 MutableState가 변경되었을 경우
- 가장 가까운 Recomposition Scope를 찾습니다.
- 하위 Composition들을 순회하며, 해당 State의 value를 읽어들이는 Composable이 있다면 Recomposition 시킵니다.
- 해당 Composable의 하위 Composable들의 parameter가 stable하며 값이 변경되지 않았는지 확인합니다.
- 만약 paramter가 stable하지 않거나 값이 변경되었을 경우, 해당 Composable도 Recomposition 시킵니다.
위와 같이 State가 변경되었을 때는, 실질적으로 State의 value를 읽는 Scope부터 Recomposition을 수행하는 듯합니다.
🌹 공식문서에서도 Jetpack Compose는 State<T>를 추적하여 특정 State<T>를 읽거나 호출하는 Composition의 composable들 중 Skip할 수 없는 모든 Composable들을 실행한다라고 언급하고 있습니다.
실제 Recomposition Count를 찍어보았을 때,
Child1과 Child2는 Recomposition skip Count도 찍히지 않았지만 하위의 Leaf1과 Leaf2는 State.value를 직접 사용하고 있기 때문에 Recomposition이 발생하고 있습니다.
만약 Child1과 Leaf1 Component를 아래와 같이 수정하게 되면 Recomposition이 어떻게 발생할까요?
@Composable
fun Child1(state: MutableState<Int>) {
println("child 1 recomposition")
Leaf1(state = state.value)
}
@Composable
fun Leaf1(state: Int) {
println("Leaf1 recomposition")
Text(text = "$state")
}
예상하였듯이, 실질적으로 State.value를 읽어들이는 Child에서 Recomposition이 발생하게 되고 하위의 Leaf1 Component는 parameter로 받는 state가 stable type이지만 값이 변경되었기 때문에 마찬가지로 Recomposition이 발생하게 됩니다.
2.2 MutableState가 아닌 일반 변수가 변경되었을 때는?
일반적으로 Recompmosition는 State<T> 객체의 변경에 의해 Trigger 되므로, 일반적인 변수가 변경되었다고 해서 Recomposition이 발생하지는 않습니다.
그렇다면 만약 일반 변수가 MutableState의 값에 영향을 받는다면 어떨까요?
👇🏻 예를 들어 이렇게!
val state = remember { mutableStateOf(0) }
var temp = state.value
이는 2.1에서의 상황과 동일하다고 생각할 수 있습니다.
일반 변수가 MutableState 값에 의해 변경된다는 것은 실질적으로 보면 MutableState의 값을 읽고 있다는 의미이기 때문이죠.
2.3 ++ Recomposition Skip count
Parent Composable에서는 Recomposition이 일어났지만, 하위 Composable은 Recomposition을 skip한 경우에 찍히게 됩니다.
@Composable
fun Child1(state: MutableState<Int>) {
val value = state.value
println("child 1 recomposition")
Leaf1(state = value)
Leaf1(state = 3)
}
@Composable
fun Leaf1(state: Int) {
println("Leaf1 recomposition")
Text(text = "$state")
}
위와 같은 경우 Child1은 Recomposition이 이루어지지만, Leaf1(state = 3)는 parameter의 값이 변경되지 않았기 때문에 Recomposition할 필요가 없어 skip되게 됩니다.
지금까지 Composable에서 Recomposition이 발생하는 경우에 대해 알아보았습니다.
그저 돌아가는 코드를 짜는 것이 아닌 성능을 생각한 코드를 짜보려고 노력해보고 있지만,
역시 쉽지 않네요. 😭
지금은 코드를 직접 짜보면서 동작 방식에 대해 유추해보고 있지만
이후에는 Compose의 내부동작에 대해 좀 더 딥하게 공부해보고 글을 수정해보도록 하겠습니다.👍🏻
감사합니다. 😌
'Android > Android' 카테고리의 다른 글
[Compose] Font에 적용되는 Padding 제거하기 (0) | 2023.04.04 |
---|---|
[Android] Room을 이용해 만든 Database와 dao는 어떻게 생겼을까? (0) | 2023.04.01 |
[Android] Retrofit의 ThreadPool : 왜 Retrofit은 Dispatcher 변경을 하지 않아도 되는것일까.. (0) | 2023.03.10 |
[Android/Compose] Compose에서 Paging3 적용기 (0) | 2023.03.03 |
[공식문서 열어보기] AlarmManger로 정확한 시간에 알림 띄워주자 (0) | 2023.02.13 |