Recomposer 생성
Compostion을 생성할 때에는 해당 composition에 대한 부모를 함께 제공해야 한다.
따라서, root composition의 부모가 Recomposer가 되어야 하는 경우 Recomposer를 생성해야 하는 상황이 생기기도 한다.
Android의 ViewGroup에서 Compose를 사용하려면 ViewGroup.setContent()를 호출하며,
궁극적으로 몇 가지 과정을 우회하여 아래의 코드와 같이 Recomposer 팩토리에게 상위 콘텍스트 생성 작업을 위임한다.
fun interface WindowRecomposerFactory {
fun createRecomposer(windowRootView: View): Recomposer
companion object {
val LifecycleAware: WindowRecomposerFactory = WindowRecomposerFactory { rootView ->
rootView.createLifecycleAwareViewTreeRecomposer()
}
}
}
위 코드에서 WindowRecomposerFactory 팩토리는 현재 연결되어 있는 window의 recomposer를 생성하는 함수를 가지고 있다.
createRecomposer를 호출하려면 root view에 대한 참조를 전달해야 하고,
이를 통해 Recomposer는 수명 주기를 인식할 수 있게 되므로 view 계층 구조의 루트에 있는 ViewTreeLifecycleOwner에 연결되는 것을 의미한다.
반대로 view 트리가 연결되지 않는 경우엔 Recomposer를 종료할 수 있으며, 이는 recomposition 프로세스의 누수를 방지하는데 중요한 역할을 한다.
Recomposition 프로세스
recomposer.runRecomposeAndApplyChanges() 함수가 호출되면 invalidation을 기다리기 시작하며 invalidation이 실행되면 자동적으로 recomposition이 일어나게 된다.
1. 먼저 runRecomposeAndApplyChanges()이 호출될 때 가장 먼저 해당 변경 사항의 전파 작업에 관찰자를 등록하는 일이다.
이를 통해 관찰자가 활성화 되어 일어나는 모든 변경 사항을 스냅샷의 형태로 invalidation목록에 추가되고, 관련 Composer에게 전파한다.
이렇게 전파된 내용을 통해 composition의 어떤 부분이 recomposition되어야 하는지에 대한 판단 근거가 된다.
2. recomposer는 모든 composition에 대해 invalidation을 수행하고, 모든 것이 변경되었다고 가정한다.
해당 시점 이전에 발생한 모든 변경 사항은 추적되지 않기 때문에 incalidation이 수행된 시간을 기점으로 처음부터 기록을 시작하며 recomposition을 위한 작업이 준비될 때까지 작업이 지연된다.
3. Recomposer 생성 시 제공된 모노리틱 클럭을 사용하여(모노리틱 클럭 이란 OS나 하드웨어가 계산하는 불변성질의 시간) parentFrameClick.withFrameNanos {} 블록을 호출하고 잠재적 대기자(가령 애니메이션과 같은)에 대해 모노리틱 클럭 프레임을 전달한다.
이를 통해 추적이 필요해지고, 이로 인해 새로운 invalidation이 발생할 수 있다.
4. 이제 Recomposer는 마지막 recomposition 호출 이후 수정된 모든 상태 값을 가져와 Composer의 모든 변경 사항을 보류 중인 recomposition으로 기록한다.
Recomposition이란 지금까지 살펴본 바와 같이 Composition 상태(슬롯 테이블)와 구체화된 트리(Applier)에 필요한 모든 변경 사항들을 고려하여 다시 계산하는 것을 의미한다.
Recomposition의 동시성
Recomposer에는 recomposition을 동시에 수행할 수 있는 기능이 있다.
바로 runRecomposeConcurrentAndApplyChanges라는 함수를 통해 recomposition을 동시에 수행하는 것인데,
이 함수는 상태 스냅샷의 invalidation을 기다리고 runRecomposeAndApplyChanges 함수와 같이 동적으로 recomposition을 트리거하는 suspend 함수이다.
suspend fun runRecomposeConcurrentAndApplyChanges(
recomposeCoroutineContext: CoroutineContext
){}
위 함수는 전달된 context를 사용하여 자체적인 CoroutineScope를 생성한다.
Recomposer의 상태
Recomposer는 수명주기를 나타내는 상태를 가진다.
enum class State{
ShutDown,
ShuttingDown,
Inactive,
InactivePEndingWork,
Idle,
PendingWork
}
각 상태의 의미는 다음과 같다.
- ShutDown: Recomposer가 취소되고 정리 작업이 완료되었으며 더 이상 사용 불가.
- ShuttingDown: Recomposer가 취소되었지만 여전히 정리 작업 수행 중이며 사용 불가.
- Inactive: Recomposer는 Composer의 invalidation을 무시하며, 이에 따라 recomposition을 트리거하지 않음.
- InactivePendingWork: Recomposer가 비활성 상태이지만 이미 프레임을 기다리는 보류 중인 이펙트가 있을 가능성이 있다. 프레임은 Recomposer가 실행을 시작하자마자 생성됨.
- Idle: Recomposer가 composition 및 스냅샷 invalidation을 추적하고 있지만 현재 수행할 작업이 없다.
- PendingWork: Recomposer가 보류 중인 작업에 대해 알림을 받았으며, 이미 작업을 수행 중 이거나 수행할 기회를 기다리고 있다.
'Android' 카테고리의 다른 글
[Android] Compose UI - (2) (0) | 2024.12.26 |
---|---|
[Android] Compose UI - (1) (0) | 2024.12.16 |
[Android] Compose 런타임 - (11) (0) | 2024.12.11 |
[Android] Compose 런타임 - (10) (0) | 2024.12.09 |
[Android] Compose 런타임 - (9) (0) | 2024.12.05 |