Rozwiąż problemy ze stabilnością

w przypadku niestabilnej klasy, która powoduje wydajność; problemów, zadbaj o to, by była stabilna. W tym dokumencie opisujemy kilka metod, które można wykorzystać.

Włącz silne pomijanie

Najpierw spróbuj włączyć silny tryb pomijania. Silny tryb pomijania umożliwia pomijanie elementów kompozycyjnych z niestabilnymi parametrami. Jest to najłatwiejszy rozwiązania problemów z wydajnością spowodowanych przez stabilność.

Więcej informacji znajdziesz w sekcji Silne pomijanie.

Ustaw klasę jako niezmienną

Możesz też spróbować uczynić klasę niestabilną całkowicie.

  • Stała: wskazuje typ, w przypadku którego wartość dowolnej właściwości nigdy nie może zostać zmiany po utworzeniu wystąpienia tego typu, a wszystkie metody w sposób niezależny od kontekstu.
    • Upewnij się, że wszystkie właściwości klasy to val, a nie var, typów stałych.
    • Typy podstawowe, takie jak String, Int i Float, są zawsze stałe.
    • Jeśli to niemożliwe, użyj stanu tworzenia dla jakichkolwiek zmiennych.
  • Stabilny: oznacza typ, który jest zmienny. Środowisko wykonawcze Compose nie dowiedzieć się, czy i kiedy jakiekolwiek publiczne właściwości lub metody tego typu da inne rezultaty w porównaniu z poprzednim wywołaniem.
.

Kolekcje stałe

Częstym powodem, dla którego funkcja Compose jest uważana za niestabilną klasę, są kolekcje. Jak zaznaczono na stronie Diagnozuj problemy ze stabilnością, kompilator Compose nie można dokładnie określić, czy kolekcje, takie jak List, Map i Set, są są naprawdę trwałe i dlatego oznaczają je jako niestabilne.

Aby rozwiązać ten problem, możesz użyć kolekcji stałych. Kompilator Compose obejmuje obsługę kolekcji stałych Kotlinx. Te kolekcji są stałe, a kompilator Compose traktuje je w taki sposób. Ta biblioteka jest nadal w wersji alfa, możesz się więc spodziewać zmian w interfejsie API.

Ponownie rozważ zastosowanie tej niestabilnej klasy przedstawionej w sekcji Zdiagnozuj stabilność :

unstable class Snack {
  
  unstable val tags: Set<String>
  
}

Możesz utworzyć stabilną wersję tags za pomocą kolekcji stałej. Na stronie zajęć zmień typ od tags do ImmutableSet<String>:

data class Snack{
    
    val tags: ImmutableSet<String> = persistentSetOf()
    
}

Po wykonaniu tej czynności wszystkie parametry klasy pozostają niezmienne, a funkcja Compose kompilator oznacza klasę jako stabilną.

Dodaj adnotację w formacie Stable lub Immutable

Możliwym sposobem rozwiązania problemów ze stabilnością jest dodanie adnotacji do klas niestabilnych z parametrem @Stable lub @Immutable.

Dodawanie adnotacji do klasy zastępuje to, co w innym przypadku kompilator wywnioskować na temat swoich zajęć. Jest to podobne do !! w Kotlin. Należy być bardzo z ostrożnością, jak ich używasz. Zastępowanie działania kompilatora może to prowadzić do nieprzewidzianych błędów, np. gdy funkcja kompozycyjna nie uruchamia się ponownie, których oczekujesz.

Jeśli można uczynić klasę stabilną bez adnotacji, wykonaj i dążenie do osiągnięcia stabilności w ten sposób.

Ten fragment kodu zawiera minimalny przykład klasy danych z adnotacjami jako stały:

@Immutable
data class Snack(

)

Niezależnie od tego, czy używasz adnotacji @Immutable czy @Stable, kompilator Compose oznacza klasę Snack jako stabilną.

Zajęcia z adnotacjami w kolekcjach

Rozważ funkcję kompozycyjną zawierającą parametr typu List<Snack>:

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  
  unstable snacks: List<Snack>
  
)

Nawet jeśli dodasz adnotacje do Snack za pomocą @Immutable, kompilator Compose nadal będzie oznaczać znaczniki parametr snacks w funkcji HighlightedSnacks jako niestabilny.

Parametry mają taki sam problem jak klasy, jeśli chodzi o typy kolekcji. Kompilator Compose zawsze oznacza parametr typu List jako niestabilny, nawet gdy jest to zbiór typów stałych.

Nie można oznaczyć pojedynczego parametru jako stabilnego ani dodać adnotacji ale kompozycyjne, by zawsze można je było pominąć. Istnieje wiele ścieżek do przodu.

Problem z niestabilnymi kolekcjami można rozwiązać na kilka sposobów. W poniższych sekcjach opisaliśmy różne podejścia.

Plik konfiguracji

Jeśli chcesz przestrzegać umowy dotyczącej stabilności w bazie kodu, możesz zdecydować się na uznanie kolekcji Kotlin jako stabilne, dodając kotlin.collections.* na plik konfiguracji stabilności.

Kolekcja stała

Aby skompilować bezpieczeństwo czasu związane z niezmiennością, możesz używa stałej kolekcji kotlinx zamiast List.

@Composable
private fun HighlightedSnacks(
    
    snacks: ImmutableList<Snack>,
    
)

Wrapper

Jeśli nie możesz użyć kolekcji stałej, możesz utworzyć własną. Aby to zrobić: umieść List w klasie stabilnej z adnotacjami. Ogólny kod jest prawdopodobnie w zależności od wymagań.

@Immutable
data class SnackCollection(
   val snacks: List<Snack>
)

Następnie możesz użyć go jako typu parametru w elemencie kompozycyjnym.

@Composable
private fun HighlightedSnacks(
    index: Int,
    snacks: SnackCollection,
    onSnackClick: (Long) -> Unit,
    modifier: Modifier = Modifier
)

Rozwiązanie

Po zastosowaniu dowolnej z tych metod kompilator Compose oznacza teraz HighlightedSnacks Możliwość tworzenia zarówno jako skippable, jak i restartable.

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  stable index: Int
  stable snacks: ImmutableList<Snack>
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
)

Podczas ponownego komponowania funkcji Utwórz może teraz pominąć HighlightedSnacks, jeśli nie ma dane wejściowe uległy zmianie.

Plik konfiguracji stabilności

Począwszy od Compose Compiler 1.5.5, pliku konfiguracji klas do powinny być stabilne i można je udostępnić podczas kompilacji. Dzięki temu klasy, których nie kontrolujesz, takie jak standardowe klasy biblioteki na przykład LocalDateTime, jako stabilną.

Plik konfiguracji jest zwykłym plikiem tekstowym z jedną klasą na wiersz. Komentarze jedno i podwójne symbole wieloznaczne. Oto przykładowa konfiguracja:

// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider kotlin collections stable
kotlin.collections.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>

Aby włączyć tę funkcję, przekaż ścieżkę pliku konfiguracji do narzędzia Compose kompilatora.

Odlotowe

kotlinOptions {
    freeCompilerArgs += [
            "-P",
            "plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
                    project.absolutePath + "/compose_compiler_config.conf"
    ]
}

Kotlin

kotlinOptions {
  freeCompilerArgs += listOf(
      "-P",
      "plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
      "${project.absolutePath}/compose_compiler_config.conf"
  )
}

Ponieważ kompilator Compose działa w każdym module w projekcie oddzielnie, możesz w razie potrzeby możesz skonfigurować różne konfiguracje różnym modułom. Możesz też utworzyć na poziomie głównym projektu i przekazać tę ścieżkę do każdego .

Wiele modułów

Innym częstym problemem jest architektura wielomodułowa. Kompilator Compose może wywnioskować, czy klasa jest stabilna, jeśli wszystkie typy inne niż podstawowe, są wyraźnie oznaczone jako stabilne lub w module, także za pomocą kompilatora Compose.

Jeśli warstwa danych znajduje się w osobnym module warstwy interfejsu, który jest zalecane podejście, może to być problem, który napotykasz.

Rozwiązanie

Aby rozwiązać ten problem, możesz zastosować jedną z tych metod:

  1. Dodaj klasy do pliku konfiguracji kompilatora.
  2. Włącz kompilator Compose w modułach warstwy danych lub otaguj zajęcia w razie potrzeby poleceniem @Stable lub @Immutable.
    • Wymaga to dodania zależności tworzenia wiadomości do warstwy danych. Pamiętaj jednak: dotyczy jedynie środowiska wykonawczego Compose, a nie środowiska wykonawczego Compose-UI
  3. W module interfejsu opakuj klasy warstwy danych w kod towarzyszący UI zajęcia.

Ten sam problem występuje też przy korzystaniu z bibliotek zewnętrznych, jeśli nie korzystają z biblioteki Kompilator tworzenia wiadomości.

Nie wszystkie elementy kompozycyjne muszą być możliwe do pominięcia

Podczas rozwiązywania problemów ze stabilnością, ale też kompozycyjne, które można pominąć. Próba wykonania tej czynności może doprowadzić do przedwczesnej optymalizacji który wprowadza więcej błędów niż naprawia.

Jest wiele sytuacji, w których możliwość pominięcia nie przynosi żadnych realnych korzyści. i może utrudniać utrzymanie kodu. Na przykład:

  • Element kompozycyjny, który nie podlega ponownej kompozycji lub w ogóle nie jest skomponowany.
  • Elementy kompozycyjne, które same w sobie nazywane są elementami kompozycyjnymi możliwymi do pominięcia.
  • Element kompozycyjny z dużą liczbą parametrów z drogimi parametrami równa się implementacji. W tym przypadku koszt sprawdzenia, czy którykolwiek parametr ma może przeważyć koszt taniej zmiany kompozycji.

Gdy element kompozycyjny jest możliwy do pominięcia, wiąże się to z niewielkim narzutem, który może nie być wart . Możesz nawet dodać adnotację do kompozycji, by nie dało się jej ponownie uruchomić, w którym stwierdzasz, że możliwość ponownego uruchomienia jest większa niż warta.