Zrozumienie fragmentu setRetainInstance (boolean)

341

Począwszy od dokumentacji:

public void setRetainInstance (boolean retain)

Kontroluj, czy instancja fragmentu jest zachowywana podczas ponownego tworzenia działania (na przykład po zmianie konfiguracji). Można tego używać tylko z fragmentami, których nie ma na tylnym stosie. Jeśli jest ustawiony, cykl życia fragmentu będzie nieco inny po odtworzeniu działania:

  • onDestroy () nie będzie wywoływany (ale nadal będzie onDetach (), ponieważ fragment jest odłączany od bieżącej aktywności).
  • Program onCreate (pakiet) nie zostanie wywołany, ponieważ fragment nie jest ponownie tworzony.
  • onAttach (Activity) i onActivityCreated (Bundle) będą nadal wywoływane.

Mam parę pytań:

  • Czy fragment również zachowuje swój widok, czy też zostanie odtworzony po zmianie konfiguracji? Co dokładnie oznacza „zachowane”?

  • Czy fragment zostanie zniszczony, gdy użytkownik opuści działanie?

  • Dlaczego nie działa z fragmentami na tylnym stosie?

  • Jakie są przypadki użycia, w których warto zastosować tę metodę?

Ixx
źródło
4
podobne pytanie z dobrą informacją: Dlaczego warto korzystać z fragmentu # setRetainInstance (boolean)?
Richard Le Mesurier,

Odpowiedzi:

348

Przede wszystkim sprawdź mój post na temat zachowanych fragmentów. To może pomóc.

Teraz, aby odpowiedzieć na twoje pytania:

Czy fragment zachowuje również stan widoku , czy też zostanie odtworzony po zmianie konfiguracji - co dokładnie jest „zachowane”?

Tak, Fragmentstan zostanie zachowany podczas zmiany konfiguracji. W szczególności „zachowane” oznacza, że ​​fragment nie zostanie zniszczony w wyniku zmian konfiguracji. Oznacza to, że Fragmentzostaną zachowane, nawet jeśli zmiana konfiguracji spowoduje Activityzniszczenie instrumentu bazowego .

Czy fragment zostanie zniszczony, gdy użytkownik opuści działanie?

Podobnie jak Activitys, Fragments może zostać zniszczony przez system, gdy zasoby pamięci są niskie. To, czy Twoje fragmenty zachowają stan instancji w trakcie zmian konfiguracji, nie będzie miało wpływu na to, czy system zniszczy je Fragmentpo opuszczeniu Activity. Jeśli opuścisz Activity(tzn. Naciskając przycisk Home), Fragments może zostać zniszczony. Jeśli wyjdziesz Activity, naciskając przycisk Wstecz (w ten sposób wywołując finish()i skutecznie niszcząc Activity), wszystkie Activitydołączone Fragments zostaną również zniszczone.

Dlaczego nie działa z fragmentami na tylnym stosie?

Prawdopodobnie jest wiele powodów, dla których nie jest obsługiwany, ale najbardziej oczywistym powodem jest to, że Activityzawiera odniesienie do FragmentManageri FragmentManagerzarządza backstackiem. Oznacza to, że bez względu na to, czy zdecydujesz się zachować swoje, Fragmentczy nie, Activity(i tym samym FragmentManagerbackstack) zostanie zniszczony przy zmianie konfiguracji. Innym powodem, dla którego może nie działać, jest to, że sytuacja może stać się trudna, jeśli zarówno zachowane fragmenty, jak i niezarezerwowane fragmenty zostaną dopuszczone do istnienia na tym samym plecaku.

Jakie są przypadki użycia, w których warto zastosować tę metodę?

Zachowane fragmenty mogą być bardzo przydatne do propagowania informacji o stanie - zwłaszcza zarządzania wątkami - między instancjami działania. Na przykład fragment może służyć jako host dla instancji Threadlub AsyncTask, zarządzając jego działaniem. Zobacz mój post na blogu na ten temat, aby uzyskać więcej informacji.

Ogólnie potraktowałbym to podobnie do używania onConfigurationChangedz Activity... nie używaj go jako bandaida tylko dlatego, że jesteś zbyt leniwy, aby poprawnie wdrożyć / obsłużyć zmianę orientacji. Używaj go tylko wtedy, gdy potrzebujesz.

Alex Lockwood
źródło
37
Obiekty widoku nie są zachowywane, zawsze są niszczone podczas zmian konfiguracji.
Markus Junginger
103
O ile mi wiadomo, jeśli masz setRetainInstance(true), Fragmentobiekt java i cała jego zawartość nie są niszczone podczas obrotu, ale widok jest odtwarzany. To się onCreatedView()nazywa ponownie. Jest to w zasadzie sposób, w jaki powinien był działać Activitiesod Androida 1.0. Nie sądzę, aby korzystanie z niego było „leniwe” lub używanie go nie jest „właściwe”. W rzeczywistości nie rozumiem, dlaczego nie jest to ustawienie domyślne lub dlaczego miałbyś chcieć go wyłączyć.
Timmmm,
24
Znajduję wyjaśnienie dla „Dlaczego to nie działa z fragmentami na tylnym stosie?” trudne do zrozumienia. Ale może jestem głupi :(
HGPB 19.10
13
@dierre Aktywność można zniszczyć na wiele sposobów. Na przykład, jeśli klikniesz „wstecz”, działanie zostanie zniszczone. Jeśli klikniesz „dom”, aktywność zostanie zatrzymana, a w przyszłości może zostać zniszczona, gdy pamięć będzie słaba. Zachowane Fragments są zachowywane tylko podczas zmian konfiguracji, w których podstawowa aktywność ma zostać zniszczona i natychmiast odtworzona. We wszystkich innych przypadkach, w których aktywność jest niszczona, zachowane fragmenty również zostaną zniszczone.
Alex Lockwood
3
@AlexLockwood czy możesz potwierdzić, że: Mimo że setRetainInstance(true)jest używany, nadal trzeba zaimplementować własną trwałość ( savedInstanceStatelub w inny sposób), aby móc obsłużyć wszystkie scenariusze: np. „Klucz domowy, obrót, powrót do aplikacji” odtwarza mój fragment z konstruktorem wywołanie, tracąc wszystkie zmienne stanu. Mam AsyncTaskzmienną jako element członkowski, dlatego chcę ją zachować, teraz, jeśli chcę, aby działała, jestem zmuszony zatrzymać zadanie, zapisać stan i wznowić po powrocie użytkownika. Podsumowując, jest to szybki sposób na pomoc w rotacji, ale ogólnie bezużyteczny.
TWiStErRob
28

setRetaininstancejest przydatny tylko wtedy, gdy twoje activityzostanie zniszczone i ponownie utworzone z powodu zmiany konfiguracji, ponieważ instancje są zapisywane podczas połączenia z onRetainNonConfigurationInstance. Oznacza to, że jeśli obrócisz urządzenie, zachowane fragmenty pozostaną na nim (nie zostaną zniszczone i ponownie utworzone), ale gdy środowisko wykonawcze zabije działanie w celu odzyskania zasobów, nic nie pozostanie. Kiedy naciśniesz przycisk Wstecz i wyjdziesz z działania, wszystko zostanie zniszczone.

Zwykle używam tej funkcji do zapisywania zmiany orientacji czasu. Powiedzmy, że pobrałem kilka bitmap z serwera, a każda z nich ma 1 MB, gdy użytkownik przypadkowo obróci swoje urządzenie, na pewno nie chcę ponownie wykonywać całej operacji pobierania. Tworzę Fragmentmapę bitową i dodam ją do menedżera i wywołania setRetainInstance, wszystkie mapy bitowe nadal tam są, nawet jeśli zmieni się orientacja ekranu.

suitianshi
źródło
Czy tworzysz fragmenty „Tylko dane” (bez żadnego widżetu) tylko jako uchwyt dla swoich bitmap, czy może te fragmenty również mają widżety? Czytałem coś o niebezpieczeństwie spowodowania wycieków pamięci, gdy fragment zawiera coś związanego z kontekstem / Aktywnością ...
hgoebl
Ramy wyczyści mActivityodniesienie dla ciebie. Ale nie wiem, czy środowisko wykonawcze wyczyści również widgety w instancji fragmentu w tym przypadku. Wypróbuj go lub zanurz się w kodzie źródłowym.
suitianshi
Miły przykład, kiedy możemy użyć setRetaininstance
Mu Sa
12

SetRetainInstance (true) pozwala przetrwać fragmentowi. Jego elementy zostaną zachowane podczas zmiany konfiguracji, takiej jak rotacja. Ale nadal może zostać zabity, gdy aktywność zostanie zabita w tle. Jeśli aktywność zawierająca w tle zostanie zabita przez system, jego instancja powinna zostać zapisana przez system, który poprawnie obsługiwałeś w SaveSstainState. Innymi słowy, onSaveInstanceState będzie zawsze wywoływany. Chociaż onCreateView nie zostanie wywołany, jeśli SetRetainInstance ma wartość true, a fragment / aktywność nie jest jeszcze zabity, nadal będzie wywoływany, jeśli zostanie zabity i będzie próbował zostać przywrócony.

Oto kilka analiz aktywności / fragmentu Androida, które mogą pomóc. http://ideaventure.blogspot.com.au/2014/01/android-activityfragment-life-cycle.html

Kejun Xia
źródło
8
Na pewno widzę, że onCreateView jest ponownie wywoływany na zachowanym fragmencie podczas obracania ekranu.
aij
Czy ten link to Twój własny blog? Powinieneś to wyjaśnić, jeśli tak jest.
Flexo
4

setRetainInstance () - Przestarzałe

Jako fragmenty Wersja 1.3.0-alpha01

Metoda setRetainInstance () w Fragments została uznana za przestarzałą. Wraz z wprowadzeniem ViewModels programiści mają specjalny interfejs API do utrzymywania stanu, który można powiązać z działaniami, fragmentami i wykresami nawigacyjnymi. Dzięki temu programiści mogą używać normalnego, nie zachowanego fragmentu i zachować osobny stan, który chcą zachować, osobno, unikając wspólnego źródła wycieków, zachowując jednocześnie użyteczne właściwości pojedynczego tworzenia i niszczenia stanu zatrzymanego (a mianowicie konstruktora ViewModel oraz wywołanie zwrotne onCleared (), które otrzymuje).

Gastón Saillén
źródło