Na tej stronie dowiesz się więcej o cyklu życia komponentu oraz o tym, jak Compose decyduje, czy komponent wymaga ponownego skompilowania.
Przegląd cyklu życia
Jak wspomnieliśmy w dokumentacji dotyczącej zarządzania stanem, Kompozycja opisuje interfejs aplikacji i jest tworzona przez uruchomienie elementów kompozycyjnych. Kompozycja to struktura drzewikowa komponentów, które opisują Twój interfejs.
Gdy Jetpack Compose po raz pierwszy uruchomi kompozycje podczas początkowej operacji kompozycji, będzie śledzić elementy kompozycyjne, które wywołujesz w celu opisania i interfejs w kompozycji. Następnie, gdy zmieni się stan aplikacji, Jetpack Tworzenie powoduje zaplanowanie zmiany kompozycji. Rekompozycja to sytuacja, w której Jetpack Compose ponownie wykonuje komponenty, które mogły ulec zmianie w odpowiedzi na zmiany stanu, a potem aktualizuje kompozycję, aby odzwierciedlić wszelkie zmiany.
Kompozycja może być wygenerowana tylko przez początkową kompozycję i zaktualizowana przez ponowne złożenie. Jedynym sposobem na zmodyfikowanie kompozycji jest jej ponowne kompozycje.
Rysunek 1. Cykl życia komponenta w kompozycji. Trafia ona do Kompozycja, zostaje przekomponowana co najmniej 0 razy i opuszcza kompozycję.
Rekompozycja jest zwykle wywoływana przez zmianę obiektu State<T>
. Utwórz
je śledzi i uruchamia wszystkie kompozycje w ramach kompozycji, które
konkretnego elementu State<T>
oraz wszelkich elementów kompozycyjnych wywoływanych przez te funkcje, których nie można
pominięte.
Jeśli funkcja kompozycyjna zostanie wywołana wiele razy, w funkcji Kompozycja. Każde wywołanie ma własny cykl życia w kompozycji.
@Composable fun MyComposable() { Column { Text("Hello") Text("World") } }
Rysunek 2. Przykład MyComposable
w kompozycji. Jeśli
funkcja kompozycyjna jest wywoływana wiele razy, wiele wystąpień jest umieszczonych w funkcji
Kompozycja. Jeśli element ma inny kolor, oznacza to, że jest
osobną instancję.
Struktura komponentu w komponentach
Wystąpienie kompozytowalnej funkcji w kompozycji jest identyfikowane przez jej miejsce wywołania. Kompilator Compose uznaje każdą witrynę generującą połączenia za osobną. Nawiązywanie połączenia z elementami kompozycyjnymi z wielu witryn generujących połączenia utworzy wiele instancji elementu kompozycyjnego w Kompozycja.
Jeśli podczas ponownej kompozycji funkcja kompozycyjna wywołuje inne elementy kompozycyjne niż w rzeczywistości. w trakcie poprzedniej kompozycji, funkcja Compose wskazuje, które kompozycje lub nie, a także dla elementów kompozycyjnych, które zostały wywołane w obu kompozycje, funkcja Kompozycje unika ich ponownego komponowania, jeśli dane wejściowe nie .
Zachowanie tożsamości ma kluczowe znaczenie dla powiązania efektów ubocznych z treściami kompozycyjnymi, dzięki czemu mogą ukończyć proces, a nie uruchamiać go ponownie zmianę kompozycji.
Przyjrzyjmy się temu przykładowi:
@Composable fun LoginScreen(showError: Boolean) { if (showError) { LoginError() } LoginInput() // This call site affects where LoginInput is placed in Composition } @Composable fun LoginInput() { /* ... */ } @Composable fun LoginError() { /* ... */ }
We fragmencie kodu powyżej funkcja LoginScreen
warunkowo wywoła funkcję
LoginError
kompozycyjny i zawsze wywołuje funkcję LoginInput
kompozycyjną. Każdy
ma unikalną witrynę wywołującą i pozycję źródłową, których kompilator używa
jednoznacznie identyfikować użytkowników.
Rysunek 3. Reprezentacja LoginScreen
w kompozycji, gdy stan się zmienia i występuje ponowne tworzenie kompozycji. Ten sam kolor oznacza, że nie została ona ponownie złożona.
Mimo że funkcja LoginInput
została wywołana jako pierwsza, a potem jako druga, instancja LoginInput
będzie zachowana w przypadku każdej rekompozycji. Dodatkowo:
ponieważ LoginInput
nie ma żadnych parametrów, które zmieniły się w
przekomponowanie, wywołanie LoginInput
zostanie pominięte przez funkcję Utwórz.
Dodawanie dodatkowych informacji, które ułatwiają inteligentne zmiany składu reklam
Wywołanie funkcji kompozycyjnej kilka razy spowoduje dodanie jej do kompozycji wielokrotnie jako cóż. W przypadku wielokrotnego wywoływania funkcji kompozycyjnej z tej samej witryny połączenia, Utwórz nie ma żadnych informacji, które pozwoliłyby jednoznacznie zidentyfikować każde wywołanie tego elementu kompozycyjnego, w celu zachowania konstrukcji zamówienia oprócz witryny wywołującej w różnych instancjach. Czasami to wystarczy, ale czasami może powodować niepożądane zachowanie.
@Composable fun MoviesScreen(movies: List<Movie>) { Column { for (movie in movies) { // MovieOverview composables are placed in Composition given its // index position in the for loop MovieOverview(movie) } } }
W powyższym przykładzie funkcja Utwórz oprócz wywołania
w celu zachowania odrębności instancji w obrębie Kompozycji. Jeśli dodano nowe pole movie
na dół listy, funkcja Utwórz może ponownie użyć instancji znajdujących się już
Od momentu, w którym ich położenie na liście się nie zmieniło,
Dane wejściowe funkcji movie
są takie same w przypadku tych instancji.
Rysunek 4. Przykład elementu MoviesScreen
w kompozycji, gdy nowy element jest dodawany na końcu listy. Komponenty MovieOverview
w kompozycji można ponownie wykorzystać. Ten sam kolor w komponowalnym elemencie MovieOverview
oznacza, że nie został on ponownie skompilowany.
Jeśli jednak lista movies
ulegnie zmianie, np. przez dodanie elementów do góry lub pośredniej pozycji na liście, usunięcie elementów lub zmianę ich kolejności, spowoduje to ponowne ułożenie wszystkich wywołań MovieOverview
, których parametr wejściowy zmienił pozycję na liście. Jest to bardzo ważne, jeśli na przykład MovieOverview
pobiera obraz filmu za pomocą efektu ubocznego. Jeśli ponowne kompozycje mają miejsce, gdy efekt jest aktywny
postęp zostanie anulowany i rozpocznie się od nowa.
@Composable fun MovieOverview(movie: Movie) { Column { // Side effect explained later in the docs. If MovieOverview // recomposes, while fetching the image is in progress, // it is cancelled and restarted. val image = loadNetworkImage(movie.url) MovieHeader(image) /* ... */ } }
Rysunek 5. Przykładowy element MoviesScreen
w kompozycji, gdy do listy dodano nowy element. Komponentów MovieOverview
nie można ponownie wykorzystać, a wszystkie efekty uboczne zostaną ponownie uruchomione. Inny kolor w MovieOverview
oznacza, że usługa została ponownie skompilowana.
Najlepiej myśleć o tożsamości instancji MovieOverview
jako:
powiązane z tożsamością movie
, która jest do niej przekazywana. Jeśli zmienimy kolejność
listę filmów, w miarę możliwości uporządkowalibyśmy w podobny sposób wystąpienia
drzewo kompozycji zamiast ponownie komponować każdy element MovieOverview
z funkcją
w innym miejscu. Dzięki funkcji Compose możesz określić w czasie wykonywania, których wartości chcesz używać do identyfikowania danej części drzewa: kompozyt key
.
Opakowując blok kodu za pomocą wywołania klucza „composable” z co najmniej jednym
przekazywane wartości, zostaną one połączone i będą używane do identyfikacji
w scenie kompozycji. Wartość pola key
nie musi wynosić
jest unikalny globalnie, musi być niepowtarzalny tylko wśród wywołań funkcji
w centrum obsługi rozmów. Zatem w tym przykładzie każdy element movie
musi mieć
key
, który jest unikalny wśród: movies
; nie ma nic złego w tym, że key
ma dostęp
i inne elementy kompozycyjne
w innym miejscu w aplikacji.
@Composable fun MoviesScreenWithKey(movies: List<Movie>) { Column { for (movie in movies) { key(movie.id) { // Unique ID for this movie MovieOverview(movie) } } } }
Nawet jeśli elementy na liście ulegną zmianie, funkcja Utwórz rozpozna
poszczególnych połączeń z MovieOverview
i możesz ich używać ponownie.
Rysunek 6. Reprezentacja elementu MoviesScreen
w kompozycjach, gdy nowy
element jest dodawany do listy. Ponieważ elementy kompozycyjne MovieOverview
mają unikalne atrybuty
kluczy, funkcja tworzenia wykrywa, które MovieOverview
instancje nie uległy zmianie, oraz
nie mogą ich ponownie wykorzystywać; efekty uboczne będą nadal realizowane.
Niektóre funkcje kompozycyjne mają wbudowaną obsługę funkcji kompozycyjnej key
. Przykład:
LazyColumn
akceptuje określenie niestandardowego elementu key
w usłudze DSL items
.
@Composable fun MoviesScreenLazy(movies: List<Movie>) { LazyColumn { items(movies, key = { movie -> movie.id }) { movie -> MovieOverview(movie) } } }
Pomijam je, jeśli dane wejściowe się nie zmieniły
Podczas zmiany kompozycji niektóre dostępne funkcje kompozycyjne mogą mieć własne atrybuty wykonanie zostało całkowicie pominięte, jeśli dane wejściowe nie zmieniły się w porównaniu z poprzednim kompozycji.
Funkcja typu „composable” może być pomijana chyba że:
- Funkcja zwraca typ inny niż
Unit
. - Funkcja jest oznaczona adnotacją
@NonRestartableComposable
lub@NonSkippableComposable
- Wymagany parametr ma niestabilny typ
Dostępny jest eksperymentalny tryb kompilatora, Strong Skipping, który łagodzi ostatnie wymaganie.
Aby typ został uznany za stabilny, musi spełniać te wymagania: umowa:
- Wynik funkcji
equals
dla 2 wystąpienia będzie zawsze taki sam dla tych samych 2 wystąpienia. - Jeśli zmieni się właściwość publiczna danego typu, zostanie powiadomiona o tym kompozycja.
- Wszystkie publiczne typy usług są też stabilne.
Istnieje kilka ważnych, typowych typów, które wliczają się do tej umowy,
Kompilator do tworzenia wiadomości będzie traktowany jako stabilny, choć nie jest wyraźnie
oznaczone jako stabilne za pomocą adnotacji @Stable
:
- Wszystkie typy wartości podstawowych:
Boolean
,Int
,Long
,Float
,Char
itp. - Strings
- Wszystkie typy funkcji (lambda)
Wszystkie te typy mogą przestrzegać umowy stabilnej, ponieważ są niezmienne. Typy niezmienne nigdy się nie zmieniają, więc nie trzeba powiadamiać Composition o zmianach, co znacznie ułatwia przestrzeganie tej umowy.
Warto zwrócić uwagę na typ MutableState
, który jest stabilny, ale może ulec zmianie. Jeśli wartość jest przechowywana w MutableState
, obiektem stanu ogólnym jest
uznaje się za stabilne, ponieważ funkcja Compose będzie powiadamiana o wszelkich zmianach
Właściwość .value
elementu State
.
Gdy wszystkie typy przekazywane jako parametry funkcji kompozycyjnej są stabilne, parametr są porównywane pod kątem równości na podstawie pozycji kompozycyjnej w interfejsie drzewo. Rekompozycja jest pomijana, jeśli wszystkie wartości są takie same jak w poprzednim wywołaniu.
Compose uznaje typ za stabilny tylko wtedy, gdy może to udowodnić. Na przykład interfejs jest zwykle traktowany jako niestabilny, a typy ze zmiennym publicznym Właściwości, których implementacji można zmienić, także nie są stabilne.
Jeśli Compose nie jest w stanie stwierdzić, że typ jest stabilny, ale chcesz, aby Compose traktował go jako stabilny, oznacz go adnotacją @Stable
.
// Marking the type as stable to favor skipping and smart recompositions. @Stable interface UiState<T : Result<T>> { val value: T? val exception: Throwable? val hasError: Boolean get() = exception != null }
We fragmencie kodu powyżej UiState
jest interfejsem, więc Compose może
zwykle uważa się ten typ za niestabilny. Dodając adnotację @Stable
, informujesz Compose, że ten typ jest stabilny, co pozwala Compose stosować inteligentne przekształcenia. Oznacza to też, że Compose będzie traktować wszystkie swoje implementacje jako stabilne, jeśli interfejs jest używany jako typ parametru.
Obecnie nie ma rekomendacji.
Zaloguj się na swoje konto Google.