Aby odpowiedzieć na to pytanie, musisz zagłębić się w LoaderManager
kod. Podczas gdy dokumentacja dla samego LoaderManagera nie jest wystarczająco jasna (lub nie byłoby tego pytania), dokumentacja LoaderManagerImpl, podklasy abstrakcyjnego LoaderManagera, jest znacznie bardziej pouczająca.
initLoader
Wywołanie, aby zainicjować określony identyfikator za pomocą modułu ładującego. Jeśli ten identyfikator ma już powiązany z nim moduł ładujący, pozostaje niezmieniony, a wszelkie poprzednie wywołania zwrotne zastępowane są nowo podanymi. Jeśli obecnie nie ma programu ładującego dla tego identyfikatora, zostanie utworzony i uruchomiony nowy program ładujący.
Tej funkcji należy zasadniczo używać podczas inicjowania komponentu, aby mieć pewność, że zostanie utworzony moduł ładujący, na którym się ona opiera. Pozwala to na ponowne wykorzystanie istniejących danych modułu ładującego, jeśli już takie istnieją, dzięki czemu na przykład w przypadku ponownego utworzenia działania po zmianie konfiguracji nie ma potrzeby ponownego tworzenia jego programów ładujących.
restartLoader
Zadzwoń, aby odtworzyć moduł ładujący powiązany z określonym identyfikatorem. Jeśli obecnie istnieje Loader powiązany z tym ID, zostanie on odpowiednio anulowany / zatrzymany / zniszczony. Zostanie utworzony nowy Loader z podanymi argumentami, a jego dane dostarczone do Ciebie, gdy będą dostępne.
[...] Po wywołaniu tej funkcji wszystkie poprzednie programy ładujące skojarzone z tym identyfikatorem zostaną uznane za nieważne i nie będziesz otrzymywać od nich dalszych aktualizacji danych.
Zasadniczo istnieją dwa przypadki:
- Program ładujący z identyfikatorem nie istnieje: obie metody utworzą nowy moduł ładujący, więc nie ma różnicy
- Program ładujący o identyfikatorze już istnieje:
initLoader
zastąpi tylko wywołania zwrotne przekazane jako parametr, ale nie anuluje ani nie zatrzyma programu ładującego. Dla a CursorLoader
oznacza to, że kursor pozostaje otwarty i aktywny (jeśli tak było przed initLoader
wywołaniem). Z drugiej strony, restartLoader anuluje, zatrzyma i zniszczy program ładujący (i zamknie podstawowe źródło danych jak kursor) i utworzy nowy moduł ładujący (który również utworzy nowy kursor i ponownie uruchomi zapytanie, jeśli moduł ładujący jest CursorLoader).
Oto uproszczony kod dla obu metod:
initLoader
LoaderInfo info = mLoaders.get(id);
if (info == null) {
info = createAndInstallLoader(id, args, LoaderManager.LoaderCallbacks<Object>)callback);
} else {
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
restartLoader
LoaderInfo info = mLoaders.get(id);
if (info != null) {
LoaderInfo inactive = mInactiveLoaders.get(id);
if (inactive != null) {
} else {
info.mLoader.abandon();
mInactiveLoaders.put(id, info);
}
}
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
Jak widać w przypadku, gdy moduł ładujący nie istnieje (info == null), obie metody utworzą nowy moduł ładujący (info = createAndInstallLoader (...)). W przypadku, gdy program ładujący już istnieje, initLoader
zastępuje tylko wywołania zwrotne (info.mCallbacks = ...), jednocześnie restartLoader
dezaktywuje stary program ładujący (zostanie zniszczony, gdy nowy program ładujący zakończy swoją pracę), a następnie tworzy nowy.
W związku z tym jest teraz jasne, kiedy initLoader
i kiedy używać restartLoader
i dlaczego warto mieć te dwie metody.
initLoader
służy do upewnienia się, że istnieje zainicjowany moduł ładujący. Jeśli żaden nie istnieje, tworzony jest nowy, jeśli już istnieje, jest ponownie używany. Zawsze używamy tej metody, chyba że potrzebujemy nowego modułu ładującego, ponieważ zmieniło się zapytanie do uruchomienia (nie dane bazowe, ale rzeczywiste zapytanie, jak w instrukcji SQL dla CursorLoader), w którym to przypadku będziemy wywoływać restartLoader
.
Aktywny / Fragment cyklu życia nie ma nic wspólnego z decyzją do korzystania z jednej lub drugiej metody (i nie ma potrzeby, aby śledzić połączeń za pomocą znacznika jeden strzał jak sugeruje Simon)! Ta decyzja jest podejmowana wyłącznie na podstawie „zapotrzebowania” na nowy program ładujący. Jeśli chcemy uruchomić to samo zapytanie, którego używamy initLoader
, jeśli chcemy uruchomić inne zapytanie, którego używamy restartLoader
.
Zawsze moglibyśmy użyć, restartLoader
ale byłoby to nieefektywne. Po obróceniu ekranu lub jeśli użytkownik opuści aplikację i wróci później do tego samego działania, zwykle chcemy pokazać ten sam wynik zapytania, więc restartLoader
niepotrzebnie ponownie utworzymy moduł ładujący i odrzucimy podstawowy (potencjalnie kosztowny) wynik zapytania.
Bardzo ważne jest, aby zrozumieć różnicę między ładowanymi danymi a „zapytaniem” w celu załadowania tych danych. Załóżmy, że używamy CursorLoader odpytującego tabelę o zamówienia. Jeśli do tej tabeli zostanie dodane nowe zamówienie, CursorLoader używa onContentChanged () do poinformowania interfejsu użytkownika o aktualizacji i wyświetleniu nowej kolejności (nie ma potrzeby używania restartLoader
w tym przypadku). Jeśli chcemy wyświetlić tylko otwarte zamówienia, potrzebujemy nowego zapytania i restartLoader
użylibyśmy do zwrócenia nowego CursorLoader odzwierciedlającego nowe zapytanie.
Czy istnieje związek między tymi dwiema metodami?
Dzielą się kodem, aby utworzyć nowy moduł ładujący, ale robią inne rzeczy, gdy program ładujący już istnieje.
Czy dzwonienie restartLoader
zawsze dzwoni initLoader
?
Nie, nigdy tak nie jest.
Czy mogę zadzwonić restartLoader
bez dzwonienia initLoader
?
Tak.
Czy można bezpiecznie zadzwonić initLoader
dwa razy, aby odświeżyć dane?
Można bezpiecznie zadzwonić initLoader
dwa razy, ale żadne dane nie zostaną odświeżone.
Kiedy powinienem użyć jednego z dwóch i dlaczego ?
To powinno (miejmy nadzieję) być jasne po moich wyjaśnieniach powyżej.
Zmiany konfiguracji
LoaderManager zachowuje swój stan niezależnie od zmian konfiguracji (w tym zmian orientacji), więc można by pomyśleć, że nie mamy już nic do zrobienia. Pomyśl jeszcze raz ...
Po pierwsze, LoaderManager nie zachowuje wywołań zwrotnych, więc jeśli nic nie zrobisz, nie będziesz otrzymywać wywołań do metod wywołania zwrotnego, takich jak onLoadFinished()
i tym podobne, a to najprawdopodobniej zepsuje twoją aplikację.
Dlatego MUSIMY wywołać przynajmniej initLoader
przywrócenie metod wywołania zwrotnego (a restartLoader
jest oczywiście również możliwe). W dokumentacji czytamy:
Jeśli w momencie wywołania wywołujący jest w stanie uruchomionym, a żądany program ładujący już istnieje i wygenerował swoje dane, wówczas wywołanie zwrotne onLoadFinished(Loader, D)
zostanie wywołane natychmiast (wewnątrz tej funkcji) [...].
Oznacza to, że jeśli zadzwonimy initLoader
po zmianie orientacji, onLoadFinished
od razu otrzymamy połączenie, ponieważ dane są już załadowane (zakładając, że tak było przed zmianą). Chociaż brzmi to prosto, może być trudne (nie wszyscy kochamy Androida ...).
Musimy rozróżnić dwa przypadki:
- Sam obsługuje zmiany konfiguracji: dotyczy to fragmentów, które używają setRetainInstance (true) lub działania z odpowiednim
android:configChanges
tagiem w manifeście. Te komponenty nie otrzymają wywołania onCreate po np. Obróceniu ekranu, więc pamiętaj o wywołaniu initLoader/restartLoader
innej metody wywołania zwrotnego (np
onActivityCreated(Bundle)
. In
). Aby móc zainicjalizować program ładujący (y), identyfikatory modułu ładującego muszą być przechowywane (np. W liście). Ponieważ komponent jest zachowywany przy zmianach konfiguracji, możemy po prostu zapętlić istniejące identyfikatory modułu ładującego i wywołać initLoader(loaderid,
...)
.
- Sam nie obsługuje zmian konfiguracji: w tym przypadku Loadery można zainicjować w onCreate, ale musimy ręcznie zachować identyfikatory modułu ładującego lub nie będziemy w stanie wykonać wymaganych wywołań initLoader / restartLoader. Jeśli identyfikatory są przechowywane w
outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)
tablicy ArrayList, wykonalibyśmy operację onSaveInstanceState i przywrócilibyśmy je w onCreate:
loaderIdsArray =
savedInstanceState.getIntegerArrayList(loaderIdsKey)
zanim wykonamy wywołanie (a) initLoader.
initLoader
(i wszystkie wywołania zwrotne zostały zakończone, program ładujący jest bezczynny) po rotacji, nie otrzymaszonLoadFinished
oddzwonienia, ale jeśli użyjeszrestartLoader
go?