W tym przewodniku omawiamy oczekiwania użytkowników dotyczące stanu UI oraz dostępne opcje. do zachowania stanu.
Zapisywanie i przywracanie stanu interfejsu aktywności wkrótce po system niszczy działania lub aplikacje są niezbędne dla dobrego użytkownika, i uzyskiwanie dodatkowych informacji. Użytkownicy oczekują, że stan UI pozostanie taki sam, ale system może zniszczenie aktywności i jej stanu w pamięci.
Aby wypełnić lukę między oczekiwaniami użytkowników a działaniem systemu, skorzystaj z szablonów kombinacji następujących metod:
ViewModel
obiektów.- Zapisano stany instancji w tych kontekstach:
- Jetpack Compose:
rememberSaveable
. - Widoki:
onSaveInstanceState()
API. - Wyświetl modele:
SavedStateHandle
.
- Jetpack Compose:
- Pamięć lokalna do zachowywania stanu interfejsu podczas przenoszenia aplikacji i aktywności.
Optymalne rozwiązanie zależy od złożoności danych interfejsu, użycia aplikacji oraz znalezienie równowagi między szybkością dostępu do danych a wykorzystaniem pamięci.
Upewnij się, że aplikacja spełnia oczekiwania użytkowników i oferuje szybką, szybką, responsywną za pomocą prostego interfejsu online. Unikaj opóźnień podczas wczytywania danych do interfejsu, zwłaszcza w przypadku częstych zmiany konfiguracji, takie jak rotacja.
Oczekiwania użytkowników i działanie systemu
W zależności od wykonywanego działania użytkownik oczekuje, że dany stan działania do usunięcia lub zachowania stanu. W niektórych przypadkach system automatycznie wykonuje to, czego oczekuje użytkownik. W pozostałych przypadkach system w przeciwieństwie do tego, czego oczekuje użytkownik.
Odrzucenie stanu interfejsu inicjowanego przez użytkownika
Użytkownik oczekuje, że po rozpoczęciu działania tymczasowy stan UI działanie pozostaje bez zmian, dopóki użytkownik nie zamknie go całkowicie. Użytkownik może całkowicie odrzucić aktywność, wykonując te czynności:
- Usunięcie aktywności z ekranu Przegląd (ostatnie).
- Zamknięcia aplikacji lub wymuszenie jej zamknięcia na ekranie ustawień.
- Ponownie uruchom urządzenie.
- Dokończenie jakiegoś procesu działania (wspomagane przez
Activity.finish()
).
W takich przypadkach odrzucenia przez użytkownika założenie jest takie, na stałe opuścili aktywność i ponownie ją otworzą; oczekuje, że aktywność rozpocznie się od „czystego stanu”. system bazowy, w przypadku tych scenariuszy odrzucenia jest zgodne z oczekiwaniami użytkownika. instancja aktywności zostanie zniszczona i usunięta z pamięci wraz ze wszystkimi zapisany w niej stan oraz wszystkie zapisane rekordy stanu instancji powiązane z działania.
Istnieją pewne wyjątki od tej reguły dotyczące całkowitego odrzucenia – na przykład użytkownik może oczekiwać, że przeglądarka wyświetli mu dokładnie tę stronę, którą przeglądał przed opuszczeniem przeglądarki za pomocą przycisku Wstecz.
Odrzucenie stanu interfejsu inicjowanego przez system
Użytkownik oczekuje, że stan interfejsu działania pozostanie niezmieniony przez cały zmian konfiguracji, takich jak obrót lub przejście do trybu wielu okien. Domyślnie jednak system niszczy aktywność, gdy taka konfiguracja i zostaną usunięte wszystkie stany interfejsu użytkownika zapisane w instancji aktywności. Do aby dowiedzieć się więcej o konfiguracjach urządzeń, zobacz Strona z informacjami o konfiguracji. Uwaga: jest to możliwe (ale niezalecane), aby zastąpić domyślne zachowanie przy zmianach konfiguracji. Patrz sekcja Obsługa zmień konfigurację samodzielnie, by dowiedzieć się więcej.
Użytkownik oczekuje też, że stan interfejsu Twojej aktywności pozostanie taki sam, jeśli: tymczasowo przełączyć się na inną aplikację, a potem wrócić do niej później. Dla: np. użytkownik przeprowadza wyszukiwanie w Twojej aktywności związanej z wyszukiwaniem, a następnie naciska klawisz przycisk ekranu głównego lub odbierze połączenie telefoniczne, gdy użytkownik wróci do aktywności związanej z wyszukiwaniem spodziewa się znaleźć tam słowa kluczowego i jego wyników – dokładnie tak, jak wcześniej.
W takim przypadku aplikacja jest umieszczona w tle, przy czym system da radę aby zachować proces aplikacji w pamięci. System może jednak zniszczyć procesu aplikacji, gdy użytkownik jest poza domem i nie wchodzi w interakcję z innymi aplikacjami. W takim instancja działania zostanie zniszczona wraz ze wszystkimi zapisanymi w niej stanem. Gdy użytkownik ponownie uruchamia aplikację, aktywność jest nieoczekiwanie czyste. Więcej informacji o śmierci procesów znajdziesz w artykule Procesy i cykl życia aplikacji.
Opcje zachowywania stanu UI
Gdy oczekiwania użytkownika dotyczące stanu UI nie odpowiadają domyślnemu systemowi musisz zapisać i przywrócić stan interfejsu użytkownika, aby zniszczenie inicjowane przez system jest widoczne dla użytkownika.
Każda z opcji zachowania stanu interfejsu różni się w zależności od tych wymiarów: wpływające na wrażenia użytkownika.
Wyświetl model | Stan zapisanej instancji | Pamięć trwała | |
---|---|---|---|
Lokalizacja pamięci | w pamięci | w pamięci | na dysku lub w sieci |
Zmiana konfiguracji przetrwania | Tak | Tak | Tak |
przetrwa śmierć procesu zainicjowanego przez system; | Nie | Tak | Tak |
Dalej obowiązuje odrzucenie wykonane przez użytkownika/onFinish() | Nie | Nie | Tak |
Ograniczenia danych | złożone obiekty są odpowiednie, ale przestrzeń jest ograniczona przez dostępną pamięć | tylko w przypadku typów podstawowych oraz prostych, małych obiektów, takich jak String. | ograniczone tylko przez miejsce na dysku lub koszt / czas pobierania z zasobu sieciowego |
Czas odczytu/zapisu | szybki (tylko dostęp do pamięci) | wolno (wymaga serializacji/deserializacji) | wolne (wymaga dostępu do dysku lub transakcji sieciowej) |
Używaj ViewModel do obsługi zmian konfiguracji
Model widoku danych idealnie nadaje się do przechowywania danych związanych z interfejsem użytkownika i zarządzania nimi, gdy aktywne korzystanie z aplikacji. Zapewnia szybki dostęp do danych interfejsu i pomaga unikanie ponownego pobierania danych z sieci lub dysku między rotacją, zmianą rozmiaru okna inne typowe zmiany konfiguracji. Aby dowiedzieć się, jak zaimplementować ViewModel: zapoznaj się z przewodnikiem ViewModel.
ViewModel zachowuje dane w pamięci, co oznacza, że ich pobranie jest tańsze niż z dysku lub sieci. Model widoku danych jest powiązany z działaniem (lub inny właściciel cyklu życia) – pozostaje w pamięci podczas konfiguracji a system automatycznie powiąże ViewModel z nowym wystąpienia aktywności, które jest wynikiem zmiany konfiguracji.
Modele View są automatycznie niszczone przez system, gdy użytkownik wycofuje się
Twojej aktywności lub fragmentu albo wywołujesz funkcję finish()
, która oznacza, że stan to
są usuwane zgodnie z oczekiwaniami użytkownika w tych sytuacjach.
W przeciwieństwie do zapisanego stanu instancji modele ViewModel są niszczone podczas inicjowania przez system
śmierć procesu. Aby ponownie załadować dane po zainicjowanym przez system śmierci procesu w
ViewModel, użyj interfejsu API SavedStateHandle
. Jeśli dane są też
związane z interfejsem użytkownika i nie muszą być trzymane w modelu widoku danych, należy użyć funkcji
onSaveInstanceState()
w systemie widoku lub rememberSaveable
w Jetpack
Utwórz. Jeśli dane to dane aplikacji, lepiej jest je zachować
na dysk.
Jeśli masz już rozwiązanie w pamięci do zapisywania stanu interfejsu użytkownika między zmianami konfiguracji może nie być konieczne używanie modelu ViewModel.
Używaj stanu zapisanej instancji jako kopii zapasowej do obsługi śmierci procesu inicjowanego przez system
wywołanie zwrotne onSaveInstanceState()
w systemie widoku danych,
rememberSaveable
w Jetpack Compose i SavedStateHandle
w
Modele widoków przechowują dane potrzebne do ponownego załadowania stanu kontrolera interfejsu, np.
działania lub fragmentu, jeśli system zniszczy je, a następnie ponownie odtworzy
kontrolerem. Aby dowiedzieć się, jak wdrożyć stan zapisanej instancji za pomocą
onSaveInstanceState
, przeczytaj sekcję Zapisywanie i przywracanie stanu aktywności w
Przewodnik dotyczący cyklu życia aktywności.
Zapisane pakiety stanów instancji są zachowywane zarówno w wyniku zmian konfiguracji, śmierci w procesach, ale są ograniczone pamięcią i szybkością, ponieważ różne interfejsy API zserializować dane. Serializacja może zużywać dużo pamięci, jeśli obiekty są bardzo skomplikowane. Ponieważ ten proces odbywa się w wątku głównym podczas zmiany konfiguracji długotrwałe serializacji może spowodować i zacinanie się obrazu.
Nie używaj zapisanego stanu instancji do przechowywania dużych ilości danych, takich jak mapy bitowe,
ani złożonych struktur danych, które wymagają długiej serializacji lub
deserializacja. Przechowuj tylko typy podstawowe oraz proste, małe obiekty
na przykład String
. W związku z tym używaj zapisanego stanu instancji do przechowywania
niezbędne do odtworzenia danych użytkownika, np. identyfikatora
w przypadku awarii innych mechanizmów trwałości. Większość
aplikacje powinny wdrożyć to rozwiązanie, aby obsługiwać proces zainicjowany przez system.
W zależności od przypadków użycia aplikacji może nie być konieczne używanie zapisanych instancji stanu. Przeglądarka może na przykład wyświetlić użytkownikowi dokładnie tę stronę, stronę przeglądaną przed opuszczeniem przeglądarki. Jeśli Twoja aktywność działa w ten sposób, możesz zrezygnować z użycia zapisanego stanu instancji i zamiast tego wszystko lokalnie.
Poza tym gdy otworzysz aktywność z intencji, pakiet dodatków będzie miał jest dostarczana do aktywności zarówno po zmianie konfiguracji, jak i po zmianie system przywraca aktywność. Jeśli dane o stanie interfejsu użytkownika, takie jak wyszukiwanie były przekazywane jako dodatkowe intencje przy uruchamianiu działania, może użyć pakietu dodatków zamiast pakietu zapisanego stanu instancji. Aby się uczyć więcej informacji o dodatkach do intencji, przeczytaj artykuł o filtrach intencji i zamiarów.
W każdym z tych scenariuszy należy nadal używać narzędzia ViewModel
, aby unikać
marnowanie czasu na ponowne ładowanie danych z bazy danych podczas zmiany konfiguracji.
Jeśli dane interfejsu użytkownika są proste i niepełne, możesz użyć zapisanych interfejsów API stanu instancji w celu zachowania danych o stanie.
Łączenie z zapisanym stanem za pomocą SavedStateRegistry
Rozpoczyna się od Fragmentu 1.1.0 lub jego zależność przejściowa Activity
1.0.0, kontrolery UI, takie jak Activity
lub Fragment
, implementują
SavedStateRegistryOwner
i zastosuj SavedStateRegistry
,
powiązane z tym kontrolerem. SavedStateRegistry
umożliwia podłączanie komponentów
zapisany stan kontrolera UI, aby mógł być wykorzystywany lub wspomagany. Przykład:
moduł Saved State dla modelu ViewModel używa SavedStateRegistry
do utworzenia
SavedStateHandle
i przekaż go obiektom ViewModel
. Możesz pobrać
SavedStateRegistry
z poziomu kontrolera UI, wywołując
getSavedStateRegistry()
Komponenty, które przyczyniają się do zmiany stanu, muszą zaimplementować
SavedStateRegistry.SavedStateProvider
, która definiuje pojedynczą metodę
pod tytułem saveState()
. Metoda saveState()
umożliwia komponentowi:
zwraca element Bundle
zawierający dowolny stan, który powinien zostać zapisany z tego komponentu.
SavedStateRegistry
wywołuje tę metodę na etapie zapisywania interfejsu użytkownika.
w cyklu życia kontrolera.
Kotlin
class SearchManager : SavedStateRegistry.SavedStateProvider { companion object { private const val QUERY = "query" } private val query: String? = null ... override fun saveState(): Bundle { return bundleOf(QUERY to query) } }
Java
class SearchManager implements SavedStateRegistry.SavedStateProvider { private static String QUERY = "query"; private String query = null; ... @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); bundle.putString(QUERY, query); return bundle; } }
Aby zarejestrować SavedStateProvider
, zadzwoń pod numer registerSavedStateProvider()
:
SavedStateRegistry
, przekazując klucz do powiązania z danymi dostawcy jako
jak również usługodawcy. Zapisane wcześniej dane dostawcy można
pobrane z zapisanego stanu przez wywołanie consumeRestoredStateForKey()
w SavedStateRegistry
, przekazując klucz powiązany z kluczem dostawcy
i skalowalnych danych.
W: Activity
lub Fragment
możesz zarejestrować SavedStateProvider
w
onCreate()
po rozmowie z: super.onCreate()
. Możesz też ustawić
LifecycleObserver
na elemencie SavedStateRegistryOwner
, który implementuje
LifecycleOwner
i zarejestruj SavedStateProvider
po
Występuje zdarzenie ON_CREATE
. Za pomocą LifecycleObserver
możesz odłączyć
rejestracji i pobrania wcześniej zapisanego stanu z
SavedStateRegistryOwner
.
Kotlin
class SearchManager(registryOwner: SavedStateRegistryOwner) : SavedStateRegistry.SavedStateProvider { companion object { private const val PROVIDER = "search_manager" private const val QUERY = "query" } private val query: String? = null init { // Register a LifecycleObserver for when the Lifecycle hits ON_CREATE registryOwner.lifecycle.addObserver(LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_CREATE) { val registry = registryOwner.savedStateRegistry // Register this object for future calls to saveState() registry.registerSavedStateProvider(PROVIDER, this) // Get the previously saved state and restore it val state = registry.consumeRestoredStateForKey(PROVIDER) // Apply the previously saved state query = state?.getString(QUERY) } } } override fun saveState(): Bundle { return bundleOf(QUERY to query) } ... } class SearchFragment : Fragment() { private var searchManager = SearchManager(this) ... }
Java
class SearchManager implements SavedStateRegistry.SavedStateProvider { private static String PROVIDER = "search_manager"; private static String QUERY = "query"; private String query = null; public SearchManager(SavedStateRegistryOwner registryOwner) { registryOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> { if (event == Lifecycle.Event.ON_CREATE) { SavedStateRegistry registry = registryOwner.getSavedStateRegistry(); // Register this object for future calls to saveState() registry.registerSavedStateProvider(PROVIDER, this); // Get the previously saved state and restore it Bundle state = registry.consumeRestoredStateForKey(PROVIDER); // Apply the previously saved state if (state != null) { query = state.getString(QUERY); } } }); } @NonNull @Override public Bundle saveState() { Bundle bundle = new Bundle(); bundle.putString(QUERY, query); return bundle; } ... } class SearchFragment extends Fragment { private SearchManager searchManager = new SearchManager(this); ... }
Używaj lokalnej trwałości, aby radzić sobie ze śmiercią procesów w przypadku złożonych lub dużych danych
Trwała pamięć lokalna, taka jak baza danych lub współdzielone ustawienia, będzie nadal działać tak długo, jak długo aplikacja jest zainstalowana na urządzeniu użytkownika (chyba że użytkownik usuwa dane aplikacji). Mimo że taka pamięć lokalna działa działania inicjowane przez system i proces aplikacji mogą być kosztowne, ponieważ musi zostać odczytany z pamięci lokalnej. Często ta trwała pamięć lokalna może już być częścią Twojej aplikacji do przechowywania wszystkich danych, których nie chcesz utracić po otwarciu i zamknięciu działania.
Ani ViewModel, ani zapisany stan instancji nie są długoterminowymi rozwiązaniami do przechowywania danych. dlatego nie zastępują one pamięci lokalnej, takiej jak baza danych. Zamiast Ciebie powinni używać tych mechanizmów tylko do tymczasowego przechowywania informacji tylko o tymczasowym stanie UI oraz używanie pamięci trwałej do przechowywania innych danych aplikacji. Zobacz Przewodnik po architekturze aplikacji. , aby dowiedzieć się więcej o tym, jak wykorzystać pamięć lokalną do utrwalenia modelu aplikacji długoterminowe (np. po ponownym uruchomieniu urządzenia).
Zarządzanie stanem interfejsu: dziel i podbijaj
Możesz skutecznie zapisywać i przywracać stan interfejsu, dzieląc pracę między różnych typów mechanizmów trwałości. W większości przypadków każdy z tych mechanizmów powinien zapisywać inny typ danych używanych w ramach aktywności w zależności od wadliwej złożoności danych, szybkości dostępu i czasu użytkowania.
- Trwałość lokalna: przechowuje wszystkie dane aplikacji, których nie chcesz utracić, jeśli
możesz otworzyć i zamknąć aktywność.
- Przykład: zbiór obiektów utworów, który może zawierać pliki audio i metadanych.
ViewModel
: przechowuje w pamięci wszystkie dane potrzebne do wyświetlenia powiązany UI, stan interfejsu ekranu.- Przykład: obiekty utworu z ostatniego i ostatniego wyszukiwania wyszukiwanego hasła.
- Zapisany stan instancji: przechowuje niewielką ilość danych potrzebnych do ponownego załadowania
Stan interfejsu, jeśli system zatrzyma, a następnie ponownie utworzy interfejs. Zamiast przechowywać dane
złożone obiekty, utrwalaj złożone obiekty w pamięci lokalnej
unikalny identyfikator tych obiektów w interfejsach API zapisanych stanów instancji.
- Przykład: przechowywanie ostatniego zapytania.
Weźmy jako przykład działanie pozwalające na przeszukiwanie z biblioteki utworów. Oto jak powinny być obsługiwane różne zdarzenia:
Gdy użytkownik doda utwór, ViewModel
natychmiast przekaże dostęp do trwałego
lokalnie. Jeśli nowo dodany utwór powinien być widoczny w interfejsie,
powinien również zaktualizować dane w obiekcie ViewModel
, aby odzwierciedlić dodanie funkcji
tę piosenkę. Pamiętaj, aby wszystkie wstawienia bazy danych przeprowadzać poza wątkiem głównym.
Gdy użytkownik wyszuka jakiś utwór, złożone dane o utworze, które zostaną wczytane
w bazie danych, powinien być natychmiast zapisany w obiekcie ViewModel
w ramach
stan UI ekranu.
Gdy aktywność przechodzi w tle, a system wywołuje zapisane
interfejsów API stanu instancji, wyszukiwane hasło powinno być zapisane w zapisanym stanie instancji,
na wypadek gdyby proces się powtarzał. Ponieważ te informacje są niezbędne do wczytywania
danych aplikacji utrwalonych w tym modelu, zapisz zapytanie w modelu ViewModel
SavedStateHandle
To wszystkie informacje potrzebne do wczytywania danych
przywrócić interfejs do aktualnego stanu.
Przywróć złożone stany: ponowne składanie elementów
Gdy nadejdzie pora, aby użytkownik wrócił do aktywności, masz 2 możliwości: scenariuszy odtworzenia aktywności:
- Po zatrzymaniu przez system aktywność jest odtwarzana ponownie.
system ma zapisane zapytanie w zapisanym pakiecie stanu instancji, a interfejs użytkownika
powinno przekazać zapytanie do funkcji
ViewModel
, jeśliSavedStateHandle
nie jest używany.ViewModel
widzi, że nie ma w pamięci podręcznej wyników wyszukiwania, a delegaci wczytywania wyników wyszukiwania za pomocą podanego zapytania. - Aktywność jest tworzona po zmianie konfiguracji. Od
ViewModel
instancja nie została zniszczona,ViewModel
przechowuje wszystkie informacje w pamięci podręcznej i nie musi ponownie wysyłać zapytań do bazy danych.
Dodatkowe materiały
Więcej informacji o zapisywaniu stanów interfejsu użytkownika znajdziesz w tych materiałach.
Blogi
- ViewModels: prosty przykład
- Modele widoku: trwałość,
onSaveInstanceState()
, przywracanie stanu interfejsu użytkownika Ładowarki - Ćwiczenie z programowania dotyczące komponentów uwzględniających cykl życia Androida
Polecane dla Ciebie
- Uwaga: tekst linku wyświetla się, gdy JavaScript jest wyłączony
- Moduł Saved State dla modelu ViewModel
- Obsługa cykli życia za pomocą komponentów uwzględniających cykl życia
- Omówienie modelu ViewModel