強力なスキップモード

強スキップは、Compose コンパイラで使用できるモードです。有効にすると、コンパイラの動作が次の 2 つの方法で変更されます。

強いスキップモードを有効にする

以前のリリースの Gradle モジュールで強制スキップを有効にするには、Gradle 構成の composeCompiler ブロックに次のオプションを含めます。

android { ... }

composeCompiler {
   enableStrongSkippingMode = true
}

コンポーザブルのスキップ設定

強スキップモードにより、スキップとコンポーズ可能な関数に関して、Compose コンパイラによって通常適用される安定性ルールの一部が緩和されます。デフォルトでは、Compose コンパイラは、すべての引数に安定した値が含まれている場合、コンポーズ可能な関数をスキップ可能としてマークします。強いスキップモードでは、この動作が変更されます。

強制スキップを有効にすると、再起動可能なすべてのコンポーズ可能な関数をスキップできるようになります。これは、パラメータが不安定かどうかに関係なく適用されます。再起動できないコンポーズ可能な関数は、スキップできません。

スキップするタイミング

再コンポーズの際にコンポーザブルをスキップするかどうかを判断するために、Compose は各パラメータの値を以前の値と比較します。比較のタイプは、パラメータの安定性に依存します。

  • 不安定なパラメータは、インスタンスの等価性(===)を使用して比較されます。
  • 安定パラメータはオブジェクトの等式(Object.equals())を使用して比較されます

すべてのパラメータがこれらの要件を満たしている場合、Compose は再コンポーズ時にコンポーザブルをスキップします。

コンポーザブルで、強制的なスキップをオプトアウトすることもできます。つまり、再開可能だがスキップ不可のコンポーザブルが必要になる場合があります。この場合は、@NonSkippableComposable アノテーションを使用します。

@NonSkippableComposable
@Composable
fun MyNonSkippableComposable {}

クラスを安定としてアノテーションする

インスタンスの等価ではなくオブジェクトの等価を使用するオブジェクトが必要な場合は、対象のクラスに引き続き @Stable アノテーションを付けます。たとえば、オブジェクトのリストをすべて監視する場合、Room などのデータソースは、リスト内のいずれかが変更されるたびに、リスト内のすべてのアイテムに新しいオブジェクトを割り当てます。

ラムダのメモ化

強いスキップモードでは、コンポーザブル内のラムダのメモ化も可能になります。強力なスキップが有効になっている場合、コンポーズ可能な関数内のすべてのラムダが自動的に記憶されます。

ストロング スキップを使用する場合にコンポーザブル内のラムダをメモ化するために、コンパイラはラムダを remember 呼び出しでラップします。ラムダのキャプチャをキーとしています。

次のようなラムダがあるとします。

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = {
        use(unstableObject)
        use(stableObject)
    }
}

強力なスキップを有効にすると、コンパイラはラムダを remember 呼び出しでラップしてメモ化します。

@Composable
fun MyComposable(unstableObject: Unstable, stableObject: Stable) {
    val lambda = remember(unstableObject, stableObject) {
        {
            use(unstableObject)
            use(stableObject)
        }
    }
}

キーは、コンポーズ可能な関数と同じ比較ルールに従います。ランタイムは、インスタンスの等価性を使用して不安定なキーを比較します。オブジェクトの等価性を使用して安定したキーを比較します。

メモ化と再コンポーズ

この最適化により、再コンポーズ時にランタイムがスキップするコンポーザブルの数が大幅に増加します。メモ化がないと、再コンポーズ中にラムダ パラメータを取るコンポーザブルに新しいラムダが割り当てられる可能性が高くなります。その結果、新しいラムダには、最後のコンポジションと等しくないパラメータが含まれます。これにより、再コンポーズが行われます。

メモ化を避ける

メモ化しないラムダがある場合は、@DontMemoize アノテーションを使用します。

val lambda = @DontMemoize {
    ...
}

APK サイズ

スキップ可能なコンポーザブルをコンパイルすると、スキップできないコンポーザブルよりも多くのコードが生成されます。強制スキップを有効にすると、コンパイラはほぼすべてのコンポーザブルをスキップ可能としてマークし、すべてのラムダを remember{...} でラップします。そのため、強いスキップモードを有効にしても、アプリの APK サイズへの影響はごくわずかです。

Now In Android で強力なスキップを有効にすると、APK サイズが 4 KB 増加しました。サイズの差は、以前はスキップできなかったコンポーズ可能な関数がアプリに存在していた数に大きく依存しますが、比較的小さな差になるはずです。