Jetpack ComposeがUIを描画するまでの流れ
こちらの記事は「Jetpack Compose Phases」の内容をまとめています。
ざっくり要約
Jetpack Composeは「Composition」、「Layout」、「Drawing」3つのフェーズを通してUIを描画しています。
Compositionフェーズ: コンポーザブル関数を実行して、UIをツリー構造で表したアウトプットを出力
Layoutフェーズ: 出力されたツリー構造を使用して高さ・幅・x座標・y座標を計測後、レイアウトノードに保持
Drwingフェーズ: 出力されたツリー構造を使用して描画
パフォーマンスの観点から、Layout・Drwingフェーズは、不要な場合は発生しません。
フェーズ | 詳細 |
---|---|
Composition | コンポーザブル関数を実行して、UIをツリー構造で表したアウトプットを出力 |
Layout | 出力されたツリー構造を使用して、各コンポーネントの高さ・幅・x座標・y座標を計測 |
Drwing | 出力されたツリー構造を使用して描画 |
3つのフェーズ
Jetpack Composeは「Composition(構成・構築)」、「Layout(レイアウト)」、「Drawing(描画)」の3フェーズを通してUIを表示しています。
Composition(構成・構築)
UIとして何を表示するかを決定するフェーズです。コンポーザブル関数(@Composableが付いている関数)を実行し、表示するUIを決定します。
Layout(レイアウト)
UIの設置場所を決めます。このフェーズでは「measurement(測定)」と「placement(設置)」の2ステップを踏んでいます。ButtonやColumnなどの各要素は、自身の大きさと子要素の大きさを測定し、設置します。
Drawing(描画)
最後のフェーズでは描画の方法を決めます。
基本的に、これらは上記の順番で実行されるため、下の画像のように一方向のデータの流れとなります。
しかし、BoxWithConstraints・LazyColumn・LazyRowは、子要素のCompositionフェーズが親要素のLayoutフェーズに依存しているため、一方向のデータの流れにはなっていません。
これら3つのフェーズは毎フレームごとに発生することもあれば、パフォーマンスの観点から発生しないこともあります。
Jetpack Composeは、すべてのフェーズにおいて同じインプットから同じ結果を返す反復処理を避けるため、前回の内容を再利用できる場合はコンポーザブル関数の実行をスキップします。これにより、不要なLayoutフェーズとDrawingフェーズは発生しません。この最適化は、Jetpack Composeが各フェーズ内でのstate(状態)の読み取りを追跡しているため可能になっています。
各フェーズの深掘り
UI描画までの各フェーズがどのように行われているかをまとめています。
Compositionフェーズ
このフェーズではコンポーザブル関数を実行し、UI構成を表すツリー構造を生成します。
下記の動画はツリー構造のイメージです。緑色の丸をlayout node(レイアウトノード)と言い、次のフェーズで必要な情報を保持しています。
実際のコードを使用し、簡単に表すと下記のようになります。動画内の緑の丸で示されていたものはコンポーザブル関数であり、各コンポーザブル関数がそれぞれノードになります。
Layoutフェーズ
このフェーズでは、先ほどのCompositionフェーズで出力したツリー構造を使用して、各ノードのサイズや位置を決定します。
ツリー構造は「子要素の測定」、「子要素の測定による自サイズの決定」、「子要素の設置」に使用されます。
このフェーズの終了時点で各ノードは、高さ・幅・x座標・y座標の値を保持しています。
先ほどのツリー構造の画像を例にまとめると下記のとおりです。
- 最上部の親ノードRowは、子ノードのImageとColumnの大きさを順に測ります。
- Imageは子ノードを持っていないため、測定後にサイズをRowノードに報告します。
- Columnは子ノードを持っているため、先に子ノードの大きさを順に測ります。
- 左のTextは子ノードを持っていないため、測定後にサイズをColumnノードに報告します。
- 右のTextは子ノードを持っていないため、測定後にサイズをColumnノードに報告します。
- Columnは受け取った子ノードのサイズを使用して、自身のサイズを決定します。
幅は子ノードの中の最大値を、高さは子ノードの合計値を使用します。 - Columnの子ノードを、自身を基準として上から順に設置していきます。
- Rowは受け取った子ノードのサイズを使用して、自身のサイズを決定します。
幅は子ノードの合計値を、高さは子ノードの中の最大値を使用します。
Drawingフェーズ
先ほどのツリー構造を再度使用してスクリーンに描画していきます。
先ほどの例を使用すると下記の順番で描画されます。
- Rowの背景色などのコンテンツを描画します。
- Imageを描画します。
- Columnを描画します。
- 左のTextを描画します。
- 右のTextを描画します。
まとめ
Jetpack Composeを使用する際に、これらの概念を理解していなくてもUI構築は可能です。しかし、どのようにしてUIが描画されているかを知ることは、課題解決の糸口になると思います。
また、パフォーマンス向上などを行う際には、この概念なしでは難しくなるため、時間があるときに勉強しておくことをお勧めします。
私も勉強途中のため、偉そうに言える身分ではないですが。。。