Aby odpowiedzieć na to pytanie, musisz zagłębić się w LoaderManagerkod. 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:
initLoaderzastąpi tylko wywołania zwrotne przekazane jako parametr, ale nie anuluje ani nie zatrzyma programu ładującego. Dla a CursorLoaderoznacza to, że kursor pozostaje otwarty i aktywny (jeśli tak było przed initLoaderwywoł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, initLoaderzastępuje tylko wywołania zwrotne (info.mCallbacks = ...), jednocześnie restartLoaderdezaktywuje 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 initLoaderi kiedy używać restartLoaderi dlaczego warto mieć te dwie metody.
initLoadersł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ć, restartLoaderale 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 restartLoaderniepotrzebnie 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 restartLoaderw tym przypadku). Jeśli chcemy wyświetlić tylko otwarte zamówienia, potrzebujemy nowego zapytania i restartLoaderuż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 restartLoaderzawsze dzwoni initLoader?
Nie, nigdy tak nie jest.
Czy mogę zadzwonić restartLoaderbez dzwonienia initLoader?
Tak.
Czy można bezpiecznie zadzwonić initLoaderdwa razy, aby odświeżyć dane?
Można bezpiecznie zadzwonić initLoaderdwa 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 initLoaderprzywrócenie metod wywołania zwrotnego (a restartLoaderjest 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 initLoaderpo zmianie orientacji, onLoadFinishedod 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:configChangestagiem w manifeście. Te komponenty nie otrzymają wywołania onCreate po np. Obróceniu ekranu, więc pamiętaj o wywołaniu initLoader/restartLoaderinnej 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 otrzymaszonLoadFinishedoddzwonienia, ale jeśli użyjeszrestartLoadergo?