Ch.4 Compose UI (1/2)

Doach Gosum·2024년 6월 12일

Compose UI

컴포즈 런타임을 위한 클라이언트 라이브러리

Compose UI와 런타임의 통합

  • Composable 함수를 통해 UI를 방출(emit)하는 구현을 제공
  • 사용자가 마주할 화면의 레이아웃 트리를 구축하는 것이 목표
  • 초기 composition 및 이후 recomposition은 레이아웃 트리를 구축하고 업데이트
  • Composable 함수를 실행하고, 트리에서 노드를 삽입, 제거, 이동 또는 교체하는 변경 사 항을 예약 (예약된 변경목록)

예약된 변경 목록을 실제 트리의 변경 목록으로 매핑

  • 위 매핑 과정에서 Composition이라는 side table이 사용
  • Composition은 여러 개를 가질 수 있음

이 매핑을 수행하는 것이 Applier

Compose UI 관점에서의 Composition

  • setContent 함수는 새로운 루트 Composition을 생성 (Activity, Fragment, View)

    Root? -> 각각 독립적인 Composable 트리를 호스팅

  • Composable들은 일반적으로 서로 다른 라이브러리(foundation, material)에 속하지만, 결국 모두 Layout(compose‐ui)으로 정의되므로 같은 노드 타입, LayoutNode를 방출

    Compose는 아래 7개의 라이브러리 그룹으로 이루어져 있음 (Maven)

Compose UI 관점에서의 Subcomposition

  • Subcomposition?
    Composition은 루트 레벨에서만 존재하는 것이 아님. Composable 트리의 더 깊은 수준에서 생성될 수 있으며 부모 Composition과 연결될 수 있음

  • 루트 Composition의 부모는 Recomposer 자체

  • Subcomposition이 필요한 이유
    • 초기 composition 과정에서 특정 정보를 알 때까지 연기하기 위해
    • 하위 트리에서 생성되는 노드의 타입을 변경하기 위해

초기 Composition 과정의 지연

  • 루트가 compose 되는 시점이 아니라 레이아웃 단계에 초기 composition을 수행하도록 결정
NodeType -> class 상속관계와 유사한 개념으로 이해

서브 트리의 노드 타입 변경

  • 예시) Vector가 Subcomposition으로 모델링되는 이유 -> 부모 Composition에서 사용 가능한 CompositionLocal 중 일부에 접근하기 용이함(테마, 밀도 등)

UI에 변경사항 반영하기

구체화(materialization)에 대한 내용이며, 이는 클라이언트 라이브러리의 책임.

다양한 타입의 Applier들

Applier, AbstractApplier -> Runtime에서 제공하는 인터페이스
그 아래 -> compose-ui에서 구현하는 구현체
  • AbstractApplier -> Applier간 기본 공통로직을 구현
    • 방문한 노드를 스택(Stack)에 저장
    • 현재 방문 중인 노드에 대한 참조를 유지
  • 자식 노드들의 타입에 관계없이 동일한 탐색 로직을 유지

Compose UI가 Runtime과 안드로이드를 통합하기 위해 제공하는 두가지 Applier
• UiApplier: 대부분의 안드로이드 UI를 렌더링하는데 사용, 노드 타입을 LayoutNode로 고정, 트리의 모든 Layout을 구체화 함.
• VectorApplier: 벡터 그래픽을 렌더링하는 데 사용, 노드 유형을 VNode로 고정, 벡터 그래픽을 표현하고 구체화 함.

AndroidUI (UiApplier)는 상향식 삽입 (하향식으로 잘못 나와있는 것 같음)
VectorApplier는 하향식 삽입

벡터의 경우 부모 노드로 변경사항을 전파할 필요가 없으므로 성능 차이가 거의 없음

새로운 LayoutNode를 구체화 하기

LayoutNode

  • 순수 kotlin 클래스 -> 여러 플랫폼에서 사용 가능
안드로이드 의존성 (UI)를 제거한 의미론적 트리(semantic tree)?
  • 자식 노드 목록을 유지, 삽입, 제거, 재정렬 등의 작업 제공

전체 과정의 마무리

  • 노드가 추가되는 상황에서, 부모와 자신에 대한 재측정을 요청 to AndroidComposeView(as owner)
    • 부모, 자식 노드가 동시에 view를 invalidate해도 그리기는 한 번만 수행됨
  • 모든 요청된 노드의 재측정과 배치를 수행 (compose phase가 아니라 view의 함수)
  • 재측정 도중 루트 노드 크기가 변경되면 다시 onMeasure를 수행
  • 마지막은, 루트 LayoutNode의 draw 함수를 호출, 각각의 자식들의 draw가 호출됨.

노드 제거를 위한 변경 사항 구체화

전체적인 프로세스는 새 노드 추가와 유사한듯

노드 이동을 위한 변경 사항 구체화

  • 노드를 제거 후 다시 추가하는 과정으로 볼 수 있음

모든 노드를 지우는 변경 사항 구체화

  • 마지막 노드부터 시작하여 모든 자식을 순회하고, 모든 자식을 분리한 다음 부모에게 재측정을 요청

Compose UI에서의 측정

LayoutWrapper?

  • 각 LayoutNode는 내, 외부 래퍼를 가지고 있음
    • 외부 래퍼는 현재 노드의 측정과 그리기를 담당하며,
      내부 래퍼는 그 자식들에 대해 동일한 작업을 수행
  • 삽입되는 노드의 외부래퍼가 현재 노드(새로운 부모)의 내부래퍼로 감싸짐

측정 정책

노드가 측정되어야할 때, 해당하는 LayoutNodeWrapper는 노드를 방출할 때 제공된 측정 정책에 의존함

profile
기본 그리고 간결함

0개의 댓글