Jetpack Composeのパフォーマンス改善のためにModifier.composedからModifier.Nodeへの移行が進められています。自分の作ったライブラリもModifier.Nodeに移行したいと思って調べ始めたのですが、インターフェースや抽象クラスがたくさん出てきて全体像を把握するのが難しかったので、Modifier.Element
やModifier.Node
といった登場人物の関連を整理して、全体の構成を俯瞰できるようにしてみました。私も調べながらこの記事を書いているので、もし間違いがあったらTwitterなどでご指摘いただけるとありがたいです。
Modifier.ElementとModifier.Nodeの関係
まず、Modifier.Element
とModifier.Node
について理解しておきたい前提があります。Modifier.Element
は、Modifierチェーンを構成する要素です。Modifierチェーンとは、Modifier.fillMaxSize().clickable{}.background()....
のようにModifier修飾子をつなげた構造のことで、このチェーンの一つ一つがModifier.Element
で構成されています。しかし、Modifier.Element
が直接UIの描画に使われるわけではありません。一つのModifier.Element
につき、一つのModifier.Node
が作成され、これがLayout
に適用されることによってUIに反映されます。
このModifier.Element
とModifier.Node
の関係は、Android Dev Summit 2022の動画で説明されています。
動画から2つのスライドを引用します。
Modifier.Element
でチェーンが構成され・・・
個々のModifier.Element
からModifier.Node
が作成され、Layout
に適用されます。
クラス構成
では、この前提を踏まえた上で、Modifier.Node
とModifier.Element
の周辺のクラス図を見ていきます。
最上位に位置するのは、Modifier
インターフェースです。Modifier
には、all
、any
、foldIn
、foldOut
といったModifierチェーンを実現するためのメソッドが定義されています。
Modifier
を継承しているのが、Modifier.Element
インターフェースです。Modifier.Element
はModifierチェーンを構成する個々の要素で、all
、any
、foldIn
、foldOut
のデフォルト実装を提供しています。(Kotlinのインターフェースって、メソッドの実装を書けるんですね。)
そして、Modifier.Element
の実装(の一つ)が、ModifierNodeElement
です。ただしこれは抽象クラスなので、実際に使う時はModifierNodeElement
をさらに継承したクラスを作ることになります。Modifier.Node
対応済みのModifier修飾子(Modifier.fillMaxSize
やModifier.clickable
など)は、ModifierNodeElement
のサブクラスのオブジェクトを返します。ModifierNodeElement
は間接的にModifier
インターフェースを実装しているので、Modifierチェーンの要素になることができるのです。
ModifierNodeElement
と1対1で対応するのが、Modifier.Node
です。Modifier.Node
も抽象クラスなので、実際にはこのサブクラスを作成して使います。ModifierNodeElement.create()
がModifier.Node
オブジェクトを作成します。作成されたModifier.Node
が、Layout
関数でUIに適用されます。Modifier.Node
のonAttach
やonDetach
は、Layout
の処理に関連して呼び出されます。
Modifier.Element
にupdate
メソッドが定義されているのがポイントで、Modifier.Node
対応がパフォーマンス改善につながる所以です。UIの状態が更新されたとき、ModifierNodeElement
はupdate
メソッドでModifier.Node
を更新します。長々としたModifierチェーンを一から構成しなおすのではなく、変更があった部分だけを更新することでパフォーマンスを改善しています(だと思います)。
Modifier.Node
は、DelegatableNode
インターフェースを実装しています。delegateは「委譲する、まかせる」という意味で、DelegatableNode
は「まかせられる側」です。Modifier.Node
がDelegatableNode
を実装しているので、あるModifier.Node
から別のModifier.Node
に処理を任せることができます。これによって、既存のModifier.Node
を組み合わせて複雑なModifierを実装することができます。
以上、今回はModifier.Node
とModifier.Element
を中心に、その周辺のインターフェースやクラスの関係性について整理しました。今後、具体的な実装例なども紹介できればいいなと思っています。