Compose は、型が安定版か不安定版かを考慮します。型が不変の場合、または再コンポーズ間で値が変更されたかどうかを Compose が認識できる場合、その型は安定しています。再コンポーズ間で値が変更されたかどうかを Compose が認識できない場合、型は不安定になります。
Compose は、コンポーザブルのパラメータの安定性を使用して、再コンポーズ中にコンポーザブルをスキップできるかどうかを決定します。
- 固定パラメータ: コンポーザブルに、変更されていない固定パラメータがある場合、Compose はそれをスキップします。
- 不安定なパラメータ: コンポーザブルに不安定なパラメータがある場合、Compose はコンポーネントの親を再コンポーズするときに常に再コンポーズします。
Compose が常に再コンポーズする不必要に不安定なコンポーネントがアプリに含まれている場合、パフォーマンスの問題やその他の問題が発生することがあります。
このドキュメントでは、アプリの安定性を向上させ、パフォーマンスと全体的なユーザー エクスペリエンスを改善する方法について詳しく説明します。
不変オブジェクト
次のスニペットは、安定性と再コンポーズの背後にある一般原則を示しています。
Contact
クラスは不変のデータクラスです。これは、すべてのパラメータが val
キーワードで定義されたプリミティブであるためです。Contact
のインスタンスの作成後に、オブジェクトのプロパティの値を変更することはできません。これを行う場合は、新しいオブジェクトを作成することになります。
data class Contact(val name: String, val number: String)
ContactRow
コンポーザブルには、Contact
型のパラメータがあります。
@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
var selected by remember { mutableStateOf(false) }
Row(modifier) {
ContactDetails(contact)
ToggleButton(selected, onToggled = { selected = !selected })
}
}
ユーザーが切り替えボタンをクリックして selected
状態が変化した場合について考えてみましょう。
- Compose は、
ContactRow
内のコードを再コンポーズする必要があるかどうかを評価します。 - これにより、
ContactDetails
の引数がContact
型のみであることが確認できます。 Contact
は不変のデータクラスであるため、Compose はContactDetails
の引数が一切変更されていないことを確認します。- そのため、Compose は
ContactDetails
をスキップし、再コンポーズしません。 - 一方、
ToggleButton
の引数は変更されているため、Compose はそのコンポーネントを再コンポーズします。
変更可能なオブジェクト
上記の例では不変オブジェクトを使用していますが、可変オブジェクトを作成することもできます。次のスニペットについて考えてみましょう。
data class Contact(var name: String, var number: String)
Contact
の各パラメータが var
になったため、クラスは不変ではなくなります。プロパティが変更されても、Compose は認識しません。これは、Compose が Compose の State オブジェクトに対する変更のみを追跡するためです。
Compose は、そのようなクラスを不安定であると見なします。Compose は、不安定なクラスの再コンポーズをスキップしません。そのため、Contact
がこの方法で定義されている場合、前の例の ContactRow
は、selected
が変更されるたびに再コンポーズします。
Compose での実装
再コンポーズの際にスキップする関数を Compose が正確に決定する方法を検討することは必須ではありませんが、重要ではありません。
Compose コンパイラをコードで実行すると、各関数と型が複数のタグのいずれかでマークされます。これらのタグは、再コンポーズの際に Compose が関数または型を処理する方法を反映しています。
関数
Compose では、関数を skippable
または restartable
としてマークできます。関数を一方または両方としてマークすることも、どちらともマークしない場合もあります。
- スキップ可能: コンパイラがコンポーザブルをスキップ可能としてマークした場合、すべての引数が以前の値と等しい場合、Compose は再コンポーズ中にコンポーザブルをスキップできます。
- 再起動可能: 再起動可能なコンポーザブルは、再コンポーズを開始できる「スコープ」として機能します。つまり、この関数をエントリ ポイントにして、状態の変更後に Compose が再コンポーズのコードの再実行を開始できるようにします。
型
Compose は、型を不変または安定版としてマークします。各タイプは次のいずれかです。
- 不変: プロパティの値が変更できず、すべてのメソッドが参照的に透過である場合、Compose は型を不変としてマークします。
- すべてのプリミティブ型は不変としてマークされます。これには、
String
、Int
、Float
が含まれます。
- すべてのプリミティブ型は不変としてマークされます。これには、
- Stable: 構築後にプロパティを変更できる型を示します。これらのプロパティが実行時に変更された場合、Compose はそれらの変更を認識します。
デバッグの安定性
パラメータが変更されていないコンポーザブルを再コンポーズする場合は、まず、明確に変更可能なパラメータの定義を確認します。var
プロパティを含む型を渡すか、既知の不安定な型を使用する val
プロパティを渡すと、Compose は常にコンポーネントを再コンポーズします。
Compose で安定性に関する複雑な問題を診断する方法については、デバッグの安定性のガイドをご覧ください。
安定性に関する問題を解決する
Compose の実装に安定性をもたらす方法については、安定性の問題を解決するのガイドをご覧ください。
まとめ
全体として、次の点に注意してください。
- パラメータ: Compose は、コンポーザブルの各パラメータの安定性を判断して、再コンポーズ時にスキップするコンポーザブルを決定します。
- 即時修正: コンポーザブルがスキップされず、パフォーマンスの問題を引き起こしている場合は、まず
var
パラメータなど、不安定性の明らかな原因を確認する必要があります。 - コンパイラ レポート: コンパイラ レポートを使用して、クラスに関して推測されている安定性を確認できます。
- コレクション: Compose は常に、
List, Set
やMap
などのコレクション クラスが不安定であると見なします。これは、それらが不変であることが保証できないためです。代わりに Kotlinx 不変コレクションを使用するか、クラスに@Immutable
または@Stable
のアノテーションを付けることができます。 - その他のモジュール: Compose コンパイラが実行されないモジュールの場合、Compose は常に不安定であると見なします。必要に応じて、クラスを UI モデルクラスでラップします。
参考資料
- パフォーマンス: Compose のパフォーマンスに関するデバッグのヒントについては、ベスト プラクティス ガイドと I/O のトークをご覧ください。