Jetpack ComposeがUIを描画するまでの流れ

こちらの記事は「[Jetpack Compose Phases](https://developer.android.com/develop/ui/compose/phases?_gl=1*2mh4ey*_up*MQ..*_ga*NzM1NTQ0NjUuMTczMTkzNTY3Nw..*_ga_6HH9YJMN9M*MTczMTkzNTY3Ni4xLjAuMTczMTkzNTY3Ni4wLjAuMTEwMTU0NjkzNA..)」の内容をまとめています。

:::note info
ざっくり要約
Jetpack Composeは「Composition」、「Layout」、「Drawing」3つのフェーズを通してUIを描画しています。

Compositionフェーズ: コンポーザブル関数を実行して、UIをツリー構造で表したアウトプットを出力
Layoutフェーズ: 出力されたツリー構造を使用して高さ・幅・x座標・y座標を計測後、レイアウトノードに保持
Drwingフェーズ: 出力されたツリー構造を使用して描画

パフォーマンスの観点から、Layout・Drwingフェーズは、不要な場合は発生しません。
:::

## 3つのフェーズ

Jetpack Composeは「Composition(構成・構築)」、「Layout(レイアウト)」、「Drawing(描画)」の3フェーズを通してUIを表示しています。

### Composition(構成・構築)

UIとして何を表示するかを決定するフェーズです。コンポーザブル関数(@Composableが付いている関数)を実行し、表示するUIを決定します。

### Layout(レイアウト)

UIの設置場所を決めます。このフェーズでは「measurement(測定)」と「placement(設置)」の2ステップを踏んでいます。ButtonやColumnなどの各要素は、自身の大きさと子要素の大きさを測定し、設置します。

### Drawing(描画)

最後のフェーズでは描画の方法を決めます。

基本的に、これらは上記の順番で実行されるため、下の画像のように一方向のデータの流れとなります。
<img src="https://developer.android.com/static/develop/ui/compose/images/compose-phases.png"/>
[Jetpack Compose Phases](https://developer.android.com/develop/ui/compose/phases?_gl=1*2mh4ey*_up*MQ..*_ga*NzM1NTQ0NjUuMTczMTkzNTY3Nw..*_ga_6HH9YJMN9M*MTczMTkzNTY3Ni4xLjAuMTczMTkzNTY3Ni4wLjAuMTEwMTU0NjkzNA..) - Figure1より

しかし、BoxWithConstraints・LazyColumn・LazyRowは、子要素のCompositionフェーズが親要素のLayoutフェーズに依存しているため、一方向のデータの流れにはなっていません。

これら3つのフェーズは毎フレームごとに発生することもあれば、パフォーマンスの観点から発生しないこともあります。
Jetpack Composeは、すべてのフェーズにおいて同じインプットから同じ結果を返す反復処理を避けるため、前回の内容を再利用できる場合はコンポーザブル関数の実行をスキップします。これにより、不要なLayoutフェーズとDrawingフェーズは発生しません。この最適化は、Jetpack Composeが各フェーズ内でのstate(状態)の読み取りを追跡しているため可能になっています。

## 各フェーズの深掘り

UI描画までの各フェーズがどのように行われているかをまとめています。

### Compositionフェーズ

このフェーズではコンポーザブル関数を実行し、UI構成を表すツリー構造を生成します。
下記の動画はツリー構造のイメージです。緑色の丸をlayout node(レイアウトノード)と言い、次のフェーズで必要な情報を保持しています。
![ezgif.com-video-to-gif-converter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875851/046f291f-bd3a-c515-d3cf-57807482e156.gif)
[Jetpack Compose Phases](https://developer.android.com/develop/ui/compose/phases?_gl=1*2mh4ey*_up*MQ..*_ga*NzM1NTQ0NjUuMTczMTkzNTY3Nw..*_ga_6HH9YJMN9M*MTczMTkzNTY3Ni4xLjAuMTczMTkzNTY3Ni4wLjAuMTEwMTU0NjkzNA..) – Figure 2より

実際のコードを使用し、簡単に表すと下記のようになります。動画内の緑の丸で示されていたものはコンポーザブル関数であり、各コンポーザブル関数がそれぞれノードになります。
<img src="https://developer.android.com/static/develop/ui/compose/images/code-subsection.png" />
[Jetpack Compose Phases](https://developer.android.com/develop/ui/compose/phases?_gl=1*2mh4ey*_up*MQ..*_ga*NzM1NTQ0NjUuMTczMTkzNTY3Nw..*_ga_6HH9YJMN9M*MTczMTkzNTY3Ni4xLjAuMTczMTkzNTY3Ni4wLjAuMTEwMTU0NjkzNA..) - Figure 3より

### Layoutフェーズ

このフェーズでは、先ほどのCompositionフェーズで出力したツリー構造を使用して、各ノードのサイズや位置を決定します。
![ezgif.com-video-to-gif-converter (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875851/1bd04be2-eab1-9b6a-f978-2810d3f4e4d9.gif)
[Jetpack Compose Phases](https://developer.android.com/develop/ui/compose/phases?_gl=1*2mh4ey*_up*MQ..*_ga*NzM1NTQ0NjUuMTczMTkzNTY3Nw..*_ga_6HH9YJMN9M*MTczMTkzNTY3Ni4xLjAuMTczMTkzNTY3Ni4wLjAuMTEwMTU0NjkzNA..)– Figure 4より

ツリー構造は「子要素の測定」、「子要素の測定による自サイズの決定」、「子要素の設置」に使用されます。
このフェーズの終了時点で各ノードは、高さ・幅・x座標・y座標の値を保持しています。

先ほどのツリー構造の画像を例にまとめると下記の通りです。
<img src="https://developer.android.com/static/develop/ui/compose/images/code-subsection.png" />
[Jetpack Compose Phases](https://developer.android.com/develop/ui/compose/phases?_gl=1*2mh4ey*_up*MQ..*_ga*NzM1NTQ0NjUuMTczMTkzNTY3Nw..*_ga_6HH9YJMN9M*MTczMTkzNTY3Ni4xLjAuMTczMTkzNTY3Ni4wLjAuMTEwMTU0NjkzNA..) - Figure 3より

1. 最上部の親ノードであるRowは、子ノードのImageとColumnの大きさを順に測定
2. Imageは子ノードを持っていないため、測定後にサイズをRowノードに報告
3. Columnは子ノードを持っているため、先に子ノードの大きさを順に測定
3-1. 左のTextは子ノードを持っていないため、測定後にサイズをColumnノードに報告
3-2. 右のTextは子ノードを持っていないため、測定後にサイズをColumnノードに報告
4. Columnは受け取った子ノードのサイズを使用して、自身のサイズを決定
    幅: 子ノードの最大値
    高さ: 子ノードの合計値
5. Columnの子ノードを、自身を基準として上から順に設置
6. Rowは受け取った子ノードのサイズを使用して、自身のサイズを決定
    幅: 子ノードの合計値
    高さ: 子ノードの最大値

### Drawingフェーズ

先ほどのツリー構造を再度使用してスクリーンに描画していきます。
![ezgif.com-video-to-gif-converter (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875851/498cf3e8-e486-1f73-06d7-4f441866ad20.gif)
[Jetpack Compose Phases](https://developer.android.com/develop/ui/compose/phases?_gl=1*2mh4ey*_up*MQ..*_ga*NzM1NTQ0NjUuMTczMTkzNTY3Nw..*_ga_6HH9YJMN9M*MTczMTkzNTY3Ni4xLjAuMTczMTkzNTY3Ni4wLjAuMTEwMTU0NjkzNA..) – Figure 5より

先ほどの例を使用すると下記の順番で描画されます。
<img src="https://developer.android.com/static/develop/ui/compose/images/code-subsection.png" />
[Jetpack Compose Phases](https://developer.android.com/develop/ui/compose/phases?_gl=1*2mh4ey*_up*MQ..*_ga*NzM1NTQ0NjUuMTczMTkzNTY3Nw..*_ga_6HH9YJMN9M*MTczMTkzNTY3Ni4xLjAuMTczMTkzNTY3Ni4wLjAuMTEwMTU0NjkzNA..) - Figure 3より

1. Rowの背景色などのコンテンツを描画
2. Imageを描画
3. Columnを描画
4. 左のTextを描画
5. 右のTextを描画

![ezgif.com-video-to-gif-converter (3).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875851/62673daa-2e99-da2f-8e68-a4598e89126d.gif)
[Jetpack Compose Phases](https://developer.android.com/develop/ui/compose/phases?_gl=1*2mh4ey*_up*MQ..*_ga*NzM1NTQ0NjUuMTczMTkzNTY3Nw..*_ga_6HH9YJMN9M*MTczMTkzNTY3Ni4xLjAuMTczMTkzNTY3Ni4wLjAuMTEwMTU0NjkzNA..) – Figure 6より

## まとめ
Jetpack Composeを使用する際に、これらの概念を理解していなくてもUI構築は可能です。しかし、どのようにしてUIが描画されているかを知ることは、課題解決の糸口になると思います。
また、パフォーマンス向上などを行う際には、この概念なしでは難しくなるため、時間があるときに勉強しておくことをお勧めします。

私も勉強途中のため、偉そうに言える身分ではないですが。。。