Compose Phase

Arakene·2024년 5월 26일

순서?

  • Composition
  • Layout
  • Drawing

Composition

Composable 함수들을 실행시키며 이전에도 몇번 나온 트리구조를 생성한다.
이 UI Tree는 layout node로 이루어져있으며 이 노드들은 다음 단계에서 필요로하는 정보들을 가지고 있다.

Layout

DFS 방식으로 노드를 탐방하며 각 노드의 사이즈와 위치를 계산한다

세가지 알고리즘에 따라 동작한다.
1. 자식 사이즈 측정 -> DFS방식이 여기서 나온다 해당 노드의 자식노드를 끝가지 타고 내려가며 계산한다.
2. 사이즈 결정 -> 1단계에서 이루어진 자식들의 사이즈들을 기반으로 노드의 사이즈를 계산한다.
3. 위치 -> 크기계산이 끝난 노드들을 정책에 맞춰 배치한다. (measurePolicy)

위 작업이 끝나면 각 노드는 width, height 크기와 x, y를 가지게 된다.

Drawing

다시 UI Tree 맨 위에서부터 내려오면서 스크린에 픽셀을 찍는다.

State read in Composition

컴포지션 결과에 따라 Layout phase, Drawing Phase를 실행할 수 있으며, 콘텐츠 + 크기 + 레이아웃이 이전 값과 동일하면 스킵된다.

State read in Layout

Layout Phase는 2단계로 나누어진다.
1. measure -> 사이즈를 측정하는 단계, Moidifer.measure과 같은 메소드들을 통해 실행됨
2. place -> 실제 화면에 배치하는 단계, Modifier.offset과 같은 메소드들을 통해 실행됨

measure, place는 각 별개의 범위를 가져서 measure이 변경되었다고해서 place를 다시 호출하지는 않는다.

State read in Drawing

Canvas, Modifier.drawBehind, Modifier.drawWithContent등을 통해 이루어진다.
상태값이 변경되면 그리기 단계만 실행한다.

State read 정리

Composition 단계에서 컨텐츠가 변경되었다면 ->
Layout 실행 ->
사이즈 또는 포지션이 달라지면 ->
Draw 실행
과 같이 진행된다.

State read 최적화

기본적으로 컴포지션에 관련된 파라미터들이 변경되면 리컴포지션이 일어난다. 그렇지만 호출시기를 조정함에 따라 불필요한 리컴포지션을 방지할 수 있다.

Box {
    val listState = rememberLazyListState()

    Image(
        // ...
        // Non-optimal implementation!
        Modifier.offset(
            with(LocalDensity.current) {
                // State read of firstVisibleItemScrollOffset in composition
                (listState.firstVisibleItemScrollOffset / 2).toDp()
            }
        )
    )

    LazyColumn(state = listState) {
        // ...
    }
}

위의 예는 스크롤이 일어날 때마다 Composition Phase에서 리컴포지션이 일어나게 된다.
하지만 람다를 제공하는 방식으로 변경하면

Box {
    val listState = rememberLazyListState()

    Image(
        // ...
        Modifier.offset {
            // State read of firstVisibleItemScrollOffset in Layout
            IntOffset(x = 0, y = listState.firstVisibleItemScrollOffset / 2)
        }
    )

    LazyColumn(state = listState) {
        // ...
    }
}

레이아웃 단계 특히 place 단계에서 호출됨으로 Composition phase에서는 값을 읽지않음으로 Layout, Drawing phase에 대한 리컴포지션만 실행하면 된다.

SubcomposeLayout

위에서 나온 3가지 phase들은 반드시 순서를 지켜 실행되어야만한다.
만약 A레이아웃의 크기를 계산한 뒤 해당 크기를 이용해 B 레이아웃을 계산하고 싶은 경우
onSizeChagned, onGlobal~~ 과 같은 함수를 이용하면 제대로된 동작을 보장하지 않을 수 있고 A layout phase -> B composeition 과 같은 권장되지 않는 구조를 만들 수 있다.
따라서 이런 경우 customLayout을 생성해야한다.
Modifier.Layout을 이용하는 방법, Layout()을 구현하는 방법, SubcomposeLayout을 사용하는 방법이 있다.

언제 사용하면 좋은가?

최소 하나의 레이아웃이 다른 레이아웃의 측정값을 이용해야하는 경우에 사용하면 좋다.

Modifier Chaining

https://www.youtube.com/watch?v=OeC5jMV342A

profile
안녕하세요 삽질하는걸 좋아하는 4년차 안드로이드 개발자입니다.

0개의 댓글