Bu soruyu cevaplamak için LoaderManagerkodun içine girmeniz gerekir . LoaderManager için dokümantasyon yeterince açık olmasa da (veya bu soru olmazdı), soyut LoaderManager'ın bir alt sınıfı olan LoaderManagerImpl dokümantasyonu çok daha aydınlatıcıdır.
initLoader
Bir Yükleyici ile belirli bir kimliği başlatmak için çağrı. Bu kimlik zaten kendisiyle ilişkilendirilmiş bir Yükleyiciye sahipse, değiştirilmeden bırakılır ve önceki geri aramalar yeni sağlananlarla değiştirilir. Kimlik için şu anda bir Yükleyici yoksa, yeni bir tane oluşturulur ve başlatılır.
Bu işlev genellikle bir bileşen başlatılırken, bağlı olduğu bir Yükleyicinin oluşturulduğundan emin olmak için kullanılmalıdır. Bu, mevcut bir Yükleyicinin verilerini zaten varsa yeniden kullanmasına izin verir, böylece örneğin bir yapılandırma değişikliğinden sonra bir Etkinlik yeniden oluşturulduğunda, yükleyicilerini yeniden oluşturması gerekmez.
restartLoader
Belirli bir kimlikle ilişkilendirilmiş Yükleyiciyi yeniden oluşturma çağrısı. Şu anda bu kimlikle ilişkili bir Yükleyici varsa, uygun şekilde iptal edilecek / durdurulacak / imha edilecektir. Verilen bağımsız değişkenlere sahip yeni bir Yükleyici oluşturulacak ve verileri mevcut olduğunda size teslim edilecektir.
[...] Bu işlevi çağırdıktan sonra, bu kimlikle ilişkilendirilmiş önceki Yükleyiciler geçersiz sayılacak ve onlardan başka veri güncellemesi almayacaksınız.
Temelde iki durum vardır:
- Kimliğine sahip yükleyici mevcut değil: her iki yöntem de yeni bir yükleyici oluşturacak, dolayısıyla burada hiçbir fark yok
- Kimliğine sahip yükleyici zaten mevcut:
initLoaderyalnızca parametre olarak iletilen geri aramaların yerini alacak ancak yükleyiciyi iptal etmeyecek veya durdurmayacaktır. Bir için CursorLoaderbu, imlecin açık ve aktif kaldığı anlamına gelir ( initLoaderçağrıdan önce durum böyleyse ). Diğer yandan `restartLoader, yükleyiciyi iptal eder, durdurur ve yok eder (ve temeldeki veri kaynağını bir imleç gibi kapatır) ve yeni bir yükleyici oluşturur (bu da yeni bir imleç oluşturur ve yükleyici ise sorguyu yeniden çalıştırır). bir CursorLoader).
İşte her iki yöntem için basitleştirilmiş kod:
initLoader
LoaderInfo info = mLoaders.get(id);
if (info == null) {
// Loader doesn't already exist -> create new one
info = createAndInstallLoader(id, args, LoaderManager.LoaderCallbacks<Object>)callback);
} else {
// Loader exists -> only replace callbacks
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
restartLoader
LoaderInfo info = mLoaders.get(id);
if (info != null) {
LoaderInfo inactive = mInactiveLoaders.get(id);
if (inactive != null) {
// does a lot of stuff to deal with already inactive loaders
} else {
// Keep track of the previous instance of this loader so we can destroy
// it when the new one completes.
info.mLoader.abandon();
mInactiveLoaders.put(id, info);
}
}
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
Yükleyicinin mevcut olmaması durumunda görebileceğimiz gibi (info == null), her iki yöntem de yeni bir yükleyici oluşturacaktır (info = createAndInstallLoader (...)). Yükleyicinin zaten mevcut olması durumunda, initLoaderyalnızca geri aramaların (info.mCallbacks = ...) yerini alırken restartLoadereski yükleyiciyi devre dışı bırakır (yeni yükleyici çalışmasını tamamladığında yok edilir) ve ardından yeni bir tane oluşturur.
Böylece şimdi ne zaman initLoaderve ne zaman kullanılacağı restartLoaderve iki yönteme sahip olmanın neden mantıklı olduğu açık.
initLoaderbaşlatılmış bir yükleyici olduğundan emin olmak için kullanılır. Hiçbiri yoksa yeni bir tane oluşturulur, zaten varsa yeniden kullanılır. Yeni bir yükleyiciye ihtiyacımız OLMADIĞINDA bu yöntemi her zaman kullanırız çünkü çalıştırılacak sorgu değişti (temel alınan veriler değil, bir CursorLoader için SQL deyiminde olduğu gibi gerçek sorgu), bu durumda arayacağız restartLoader.
Etkinlik / Fragman yaşam döngüsü birini kullanmak kararı veya diğer yöntemle ilgisi yoktur (ve Simon önerildiği gibi bir atışlık bayrağını kullanarak aramaların takip etmek için gerek yoktur)! Bu karar, yalnızca yeni bir yükleyiciye "ihtiyaç" temel alınarak verilir. Kullandığımız aynı sorguyu çalıştırmak istersek initLoader, farklı bir sorgu çalıştırmak istersek kullanırız restartLoader.
Her zaman kullanabilirdik restartLoaderama bu verimsiz olurdu. Bir ekran dönüşünden sonra veya kullanıcı uygulamadan uzaklaşıp daha sonra aynı Aktiviteye dönerse, genellikle aynı sorgu sonucunu göstermek restartLoaderisteriz ve böylece yükleyiciyi gereksiz yere yeniden oluşturur ve temeldeki (potansiyel olarak pahalı) sorgu sonucunu reddeder.
Yüklenen veriler ile bu verileri yüklemek için "sorgu" arasındaki farkı anlamak çok önemlidir. Siparişler için bir tabloyu sorgulayan bir CursorLoader kullandığımızı varsayalım. Bu tabloya yeni bir sıra eklenirse, CursorLoader, UI'yi yeni sıralamayı güncellemesi ve göstermesi için bilgilendirmek için onContentChanged () öğesini kullanır ( restartLoaderbu durumda kullanmaya gerek yoktur ). Yalnızca açık siparişleri görüntülemek istiyorsak yeni bir sorguya ihtiyacımız var ve bunu restartLoaderyeni sorguyu yansıtan yeni bir CursorLoader döndürmek için kullanacağız .
İki yöntem arasında bir ilişki var mı?
Yeni bir Yükleyici oluşturmak için kodu paylaşırlar, ancak bir yükleyici zaten mevcut olduğunda farklı şeyler yaparlar.
Aramak restartLoaderher zaman arar initLoadermı?
Hayır, asla yapmaz.
Aramak restartLoaderzorunda kalmadan arayabilir miyim initLoader?
Evet.
initLoaderVerileri yenilemek için iki kez aramak güvenli mi?
initLoaderİki kez aramak güvenlidir, ancak hiçbir veri yenilenmeyecektir.
İkisinden birini ne zaman kullanmalıyım ve neden ?
Yukarıdaki açıklamalarımdan sonra (umarım) bu açık olmalıdır.
Yapılandırma değişiklikleri
LoaderManager, durumunu konfigürasyon değişiklikleri (yönelim değişiklikleri dahil) boyunca korur, böylece yapmamız gereken hiçbir şey kalmadığını düşünebilirsiniz. Tekrar düşün...
Her şeyden önce, bir LoaderManager geri aramaları saklamaz, bu nedenle hiçbir şey yapmazsanız onLoadFinished(), gibi geri arama yöntemlerinize ve benzerlerine çağrı almayacaksınız ve bu büyük olasılıkla uygulamanızı bozacaktır.
Bu nedenle initLoader, geri arama yöntemlerini geri yüklemek için en azından aramalıyız (a restartLoader, elbette, mümkündür). Dokümantasyon durumları:
Arama noktasında arayan başlatılmış durumdaysa ve istenen yükleyici zaten mevcutsa ve verilerini oluşturmuşsa, geri arama onLoadFinished(Loader, D)hemen çağrılacaktır (bu işlevin içinde) [...].
Bu, initLoaderbir oryantasyon değişikliğinden sonra ararsak onLoadFinished, veriler zaten yüklendiği için hemen bir çağrı alacağımız anlamına gelir (değişiklikten önce durumun böyle olduğunu varsayarak). Bu kulağa basit gelse de aldatıcı olabilir (hepimiz Android'i sevmiyor muyuz ...).
İki durumu birbirinden ayırmalıyız:
- Konfigürasyon değişikliklerini yönetir: Bu, setRetainInstance (true) kullanan Parçalar için veya bildiride uygun
android:configChangesetikete sahip bir Etkinlik için geçerlidir . Bu bileşenler, örneğin bir ekran rotasyonundan sonra bir onCreate çağrısı almaz, bu nedenle initLoader/restartLoaderbaşka bir geri çağrı yöntemini (örn
onActivityCreated(Bundle). İçinde) çağırmayı unutmayın
. Yükleyiciyi / Yükleyicileri başlatabilmek için, yükleyici kimliklerinin saklanması gerekir (örneğin bir Listede). Bileşen, yapılandırma değişiklikleri boyunca korunduğundan, yalnızca mevcut yükleyici kimlikleri üzerinden döngü oluşturabilir ve çağırabiliriz initLoader(loaderid,
...).
- Konfigürasyon değişikliklerini kendisi işlemez: Bu durumda, Yükleyiciler onCreate içinde başlatılabilir ancak yükleyici kimliklerini manuel olarak tutmamız gerekir, aksi takdirde gerekli initLoader / restartLoader çağrılarını yapamayız.
outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)Kimlikler bir ArrayList içinde saklanıyorsa loaderIdsArray =
savedInstanceState.getIntegerArrayList(loaderIdsKey), initLoader çağrılarını yapmadan önce onSaveInstanceState içinde bir onCreate: kimliklerini geri
yükleriz.
initLoaderBir rotasyondan sonra kullanırsanız (ve tüm geri aramalar bitmişse, Yükleyici boştadır) bironLoadFinishedgeri arama almazsınız, ancak kullanırsanızrestartLoaderalırsınız?