Bu soruyu cevaplamak için LoaderManager
kodun 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:
initLoader
yalnızca parametre olarak iletilen geri aramaların yerini alacak ancak yükleyiciyi iptal etmeyecek veya durdurmayacaktır. Bir için CursorLoader
bu, 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, initLoader
yalnızca geri aramaların (info.mCallbacks = ...) yerini alırken restartLoader
eski 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 initLoader
ve ne zaman kullanılacağı restartLoader
ve iki yönteme sahip olmanın neden mantıklı olduğu açık.
initLoader
baş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 restartLoader
ama 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 restartLoader
isteriz 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 ( restartLoader
bu durumda kullanmaya gerek yoktur ). Yalnızca açık siparişleri görüntülemek istiyorsak yeni bir sorguya ihtiyacımız var ve bunu restartLoader
yeni 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 restartLoader
her zaman arar initLoader
mı?
Hayır, asla yapmaz.
Aramak restartLoader
zorunda kalmadan arayabilir miyim initLoader
?
Evet.
initLoader
Verileri 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, initLoader
bir 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:configChanges
etikete 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/restartLoader
baş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.
initLoader
Bir rotasyondan sonra kullanırsanız (ve tüm geri aramalar bitmişse, Yükleyici boştadır) bironLoadFinished
geri arama almazsınız, ancak kullanırsanızrestartLoader
alırsınız?