Intensywny tryb pomijania

Silne pomijanie to tryb dostępny w kompilatorze Compose. Gdy ta opcja jest włączona, zmienia działanie kompilatora na 2 sposoby:

Włącz tryb silnego pomijania

W Kotlinie 2.0.20 silne pomijanie jest domyślnie włączone.

Aby włączyć silne pomijanie w module Gradle w wersji starszej niż 2.0.20, dodaj tę opcję w bloku composeCompiler konfiguracji Gradle:

android { ... }

composeCompiler {
   enableStrongSkippingMode = true
}

Możliwość pominięcia w przypadku funkcji kompozycyjnych

Tryb silnego pomijania rozluźnia niektóre reguły stabilności, które kompilator Compose zwykle stosuje w przypadku pomijania i funkcji typu „composable”. Domyślnie kompilator Compose oznacza funkcję typu „composable” jako możliwą do pominięcia, jeśli wszystkie jej argumenty mają stabilne wartości. Tryb silnego pomijania to zmienia.

Gdy włączone jest silne pomijanie, wszystkie funkcje typu „composable”, które można uruchomić ponownie, można też pominąć. Dotyczy to zarówno parametrów niestabilnych, jak i stabilnych. Funkcje typu „composable”, których nie można uruchomić ponownie, pozostają niepomijalne.

Kiedy pominąć

Aby określić, czy pominąć funkcję kompozycyjną podczas rekompozycji, Compose porównuje wartość każdego parametru z jego poprzednimi wartościami. Typ porównania zależy od stabilności parametru.

  • Niestabilne parametry są porównywane za pomocą równości instancji (===)
  • Stabilne parametry są porównywane za pomocą równości obiektów (Object.equals())

Jeśli wszystkie parametry spełniają te wymagania, Compose pomija funkcję kompozycyjną podczas rekompozycji.

Możesz chcieć, aby funkcja kompozycyjna nie korzystała z silnego pomijania. Oznacza to, że możesz chcieć, aby funkcja kompozycyjna była możliwa do ponownego uruchomienia, ale niemożliwa do pominięcia. W takim przypadku użyj adnotacji @NonSkippableComposable.

@NonSkippableComposable
@Composable
fun MyNonSkippableComposable {}

Oznaczanie klas jako stabilnych

Jeśli chcesz, aby obiekt był porównywany przy użyciu równości obiektów (Object.equals()) zamiast równości instancji (===), nadal dodawaj do danej klasy adnotację @Stable lub @Immutable.

Zanim dodasz @Stable lub @Immutable, weź pod uwagę te kompromisy:

  • Klasy ze stabilnymi właściwościami w modułach Compose są już stabilne: klasa z właściwościami val o stabilnych typach w module skompilowanym za pomocą kompilatora Compose jest automatycznie uznawana za stabilną i używa .equals() bez konieczności stosowania @Immutable lub @Stable.
  • Dodawaj adnotacje tylko do klas z prostymi kontrolami .equals(): dodaj @Stable lub @Immutable, gdy kompilator nie może wywnioskować stabilności (np. w przypadku klas z modułów innych niż Compose) i często są przydzielane nowe instancje o identycznych wartościach, pod warunkiem że kontrola .equals() klasy jest prosta.
  • Zachowaj ostrożność podczas dodawania adnotacji do modeli z kolekcjami: dodanie adnotacji @Stable lub @Immutable do modeli zawierających kolekcje (lub zastąpienie adnotacji List adnotacją ImmutableList) powoduje, że Compose porównuje każdy element w kolekcji z adnotacją .equals() przy każdym przebiegu rekompozycji. W przypadku dużych kolekcji lub modeli używanych w układach leniwych (LazyColumn, LazyRow, LazyGrid) porównywanie każdego elementu może być wolniejsze niż użycie równości instancji (===) lub bezpośrednia rekompozycja.

Zapamiętywanie funkcji lambda

Tryb silnego pomijania umożliwia też większą memoizację funkcji lambda w funkcjach kompozycyjnych. Po włączeniu silnego pomijania każda funkcja lambda w funkcji typu „composable” będzie automatycznie zapamiętywana.

Przykłady

Aby uzyskać zapamiętywanie funkcji lambda w funkcjach kompozycyjnych podczas korzystania z silnego pomijania, kompilator opakowuje funkcję lambda wywołaniem remember. Jest on powiązany z przechwytywaniem wyrażenia lambda.

Rozważmy przypadek, w którym masz funkcję lambda, jak w tym przykładzie:

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

Gdy włączone jest silne pomijanie, kompilator zapamiętuje funkcję lambda, opakowując ją w wywołanie remember:

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

Klucze podlegają tym samym regułom porównywania co funkcje typu „composable”. Środowisko wykonawcze porównuje niestabilne klucze, używając równości instancji. Porównuje stabilne klucze, używając równości obiektów.

Zapamiętywanie i rekompozycja

Ta optymalizacja znacznie zwiększa liczbę funkcji kompozycyjnych, które środowisko wykonawcze pomija podczas rekompozycji. Bez zapamiętywania środowisko wykonawcze znacznie częściej przydziela nową funkcję lambda do dowolnego elementu kompozycyjnego, który przyjmuje parametr lambda podczas rekompozycji. W rezultacie nowa funkcja lambda ma parametry, które nie są równe ostatniej kompozycji. Powoduje to rekompozycję.

Unikaj zapamiętywania

Jeśli masz funkcję lambda, której nie chcesz zapamiętywać, użyj adnotacji @DontMemoize.

val lambda = @DontMemoize {
    ...
}

Rozmiar pliku APK

Po skompilowaniu elementy kompozycyjne, które można pominąć, generują więcej kodu niż te, których nie można pominąć. Gdy włączone jest silne pomijanie, kompilator oznacza prawie wszystkie funkcje kompozycyjne jako możliwe do pominięcia i opakowuje wszystkie lambdy w remember{...}. Z tego powodu włączenie trybu silnego pomijania ma bardzo mały wpływ na rozmiar pliku APK aplikacji.

Włączenie silnego pomijania w aplikacji Now In Android zwiększyło rozmiar pliku APK o 4 kB. Różnica w rozmiarze zależy głównie od liczby wcześniej niepomijalnych elementów kompozycyjnych, które były obecne w danej aplikacji, ale powinna być stosunkowo niewielka.