Untuk menjawab pertanyaan ini, Anda perlu menggali ke dalam LoaderManager
kode. Walaupun dokumentasi untuk LoaderManager itu sendiri tidak cukup jelas (atau tidak akan ada pertanyaan ini), dokumentasi untuk LoaderManagerImpl, subkelas dari LoaderManager abstrak, jauh lebih mencerahkan.
initLoader
Panggilan untuk menginisialisasi ID tertentu dengan Loader. Jika ID ini sudah memiliki Loader yang terkait dengannya, ID itu dibiarkan tidak berubah dan panggilan balik sebelumnya diganti dengan yang baru disediakan. Jika saat ini tidak ada Loader untuk ID, yang baru dibuat dan dimulai.
Fungsi ini umumnya harus digunakan ketika suatu komponen diinisialisasi, untuk memastikan bahwa Loader yang diandalkan dibuat. Ini memungkinkannya untuk menggunakan kembali data Loader yang ada jika sudah ada, sehingga misalnya ketika suatu Aktivitas dibuat kembali setelah perubahan konfigurasi, ia tidak perlu membuat ulang loadernya.
restartLoader
Panggilan untuk membuat kembali Loader yang terkait dengan ID tertentu. Jika saat ini ada Loader yang terkait dengan ID ini, itu akan dibatalkan / dihentikan / dihancurkan sebagaimana mestinya. Loader baru dengan argumen yang diberikan akan dibuat dan datanya dikirimkan kepada Anda setelah tersedia.
[...] Setelah memanggil fungsi ini, semua Loader sebelumnya yang terkait dengan ID ini akan dianggap tidak valid, dan Anda tidak akan menerima pembaruan data lebih lanjut darinya.
Pada dasarnya ada dua kasus:
- Pemuat dengan id tidak ada: kedua metode akan membuat pemuat baru sehingga tidak ada perbedaan di sana
- Loader dengan id sudah ada:
initLoader
hanya akan mengganti callback yang dilewatkan sebagai parameter tetapi tidak akan membatalkan atau menghentikan loader. Untuk CursorLoader
itu berarti kursor tetap terbuka dan aktif (jika itu yang terjadi sebelum initLoader
panggilan). `restartLoader, di sisi lain, akan membatalkan, menghentikan, dan menghancurkan loader (dan menutup sumber data yang mendasarinya seperti kursor) dan membuat loader baru (yang juga akan membuat kursor baru dan menjalankan kembali kueri jika loader tersebut adalah a CursorLoader).
Berikut kode yang disederhanakan untuk kedua metode:
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);
Seperti yang dapat kita lihat jika loader tidak ada (info == null) kedua metode akan membuat loader baru (info = createAndInstallLoader (...)). Dalam hal loader sudah ada initLoader
hanya menggantikan callback (info.mCallbacks = ...) saat restartLoader
menonaktifkan loader lama (itu akan dihancurkan ketika loader baru menyelesaikan pekerjaannya) dan kemudian membuat yang baru.
Dengan demikian dikatakan sekarang jelas kapan harus digunakan initLoader
dan kapan harus digunakan restartLoader
dan mengapa masuk akal untuk memiliki kedua metode tersebut.
initLoader
digunakan untuk memastikan ada loader yang diinisialisasi. Jika tidak ada yang baru dibuat, jika sudah ada itu digunakan kembali. Kami selalu menggunakan metode ini KECUALI kami membutuhkan loader baru karena permintaan untuk menjalankan telah berubah (bukan data yang mendasarinya tetapi permintaan aktual seperti dalam pernyataan SQL untuk CursorLoader), dalam hal ini kami akan memanggil restartLoader
.
The Activity / Fragment siklus hidup tidak ada hubungannya dengan keputusan untuk menggunakan satu atau metode lain (dan tidak perlu untuk melacak panggilan menggunakan bendera satu-shot sebagai Simon disarankan)! Keputusan ini dibuat semata-mata berdasarkan "kebutuhan" untuk loader baru. Jika kita ingin menjalankan kueri yang sama dengan yang kita gunakan initLoader
, jika kita ingin menjalankan kueri lain yang kita gunakan restartLoader
.
Kita selalu bisa menggunakan restartLoader
tetapi itu tidak efisien. Setelah rotasi layar atau jika pengguna bernavigasi menjauh dari aplikasi dan kembali lagi ke Aktivitas yang sama, kami biasanya ingin menampilkan hasil kueri yang sama sehingga restartLoader
tidak perlu lagi membuat ulang loader dan mengabaikan hasil kueri yang mendasari (berpotensi mahal).
Sangat penting untuk memahami perbedaan antara data yang dimuat dan "kueri" untuk memuat data itu. Mari kita asumsikan kita menggunakan CursorLoader yang menanyakan tabel untuk pesanan. Jika pesanan baru ditambahkan ke tabel itu CursorLoader menggunakan onContentChanged () untuk menginformasikan UI untuk memperbarui dan menunjukkan pesanan baru (tidak perlu digunakan restartLoader
dalam kasus ini). Jika kami ingin menampilkan hanya pesanan terbuka, kami memerlukan kueri baru dan kami akan gunakan restartLoader
untuk mengembalikan CursorLoader baru yang mencerminkan kueri baru.
Apakah ada hubungan antara kedua metode tersebut?
Mereka membagikan kode untuk membuat Loader baru tetapi mereka melakukan hal yang berbeda ketika loader sudah ada.
Apakah menelepon restartLoader
selalu menelepon initLoader
?
Tidak, itu tidak pernah terjadi.
Bisakah saya menelepon restartLoader
tanpa harus menelepon initLoader
?
Iya.
Apakah aman menelepon initLoader
dua kali untuk menyegarkan data?
Aman untuk menelepon initLoader
dua kali tetapi tidak ada data yang di-refresh.
Kapan saya harus menggunakan salah satu dari keduanya dan mengapa ?
Itu seharusnya (semoga) menjadi jelas setelah penjelasan saya di atas.
Konfigurasi berubah
LoaderManager mempertahankan statusnya di seluruh perubahan konfigurasi (termasuk perubahan orientasi) sehingga Anda akan berpikir tidak ada yang tersisa untuk kami lakukan. Pikirkan lagi...
Pertama-tama, LoaderManager tidak mempertahankan panggilan balik, jadi jika Anda tidak melakukan apa pun, Anda tidak akan menerima panggilan ke metode panggilan balik seperti onLoadFinished()
dan sejenisnya dan yang kemungkinan besar akan merusak aplikasi Anda.
Karena itu kita HARUS menelepon setidaknya initLoader
untuk mengembalikan metode panggilan balik (a restartLoader
, tentu saja, mungkin juga). The dokumentasi menyatakan:
Jika pada titik panggilan pemanggil dalam keadaan mulai, dan loader yang diminta sudah ada dan telah menghasilkan datanya, maka panggilan balik onLoadFinished(Loader, D)
akan segera dipanggil (di dalam fungsi ini) [...].
Itu berarti jika kita memanggil initLoader
setelah perubahan orientasi, kita akan langsung mendapat onLoadFinished
panggilan karena data sudah dimuat (dengan asumsi itu adalah kasus sebelum perubahan). Meskipun kedengarannya langsung, itu bisa rumit (bukankah kita semua menyukai Android ...).
Kita harus membedakan antara dua kasus:
- Menangani perubahan konfigurasi itu sendiri: ini adalah kasus untuk Fragmen yang menggunakan setRetainInstance (true) atau untuk Aktivitas dengan
android:configChanges
tag yang sesuai dalam manifes. Komponen-komponen ini tidak akan menerima panggilan onCreate setelah mis. Rotasi layar, jadi ingatlah untuk memanggil
initLoader/restartLoader
metode panggilan balik lain (misalnya dalam
onActivityCreated(Bundle)
). Agar dapat menginisialisasi Loader, id loader harus disimpan (misalnya dalam Daftar). Karena komponen dipertahankan di seluruh perubahan konfigurasi kita hanya dapat mengulangi id loader dan panggilan yang ada initLoader(loaderid,
...)
.
- Tidak menangani perubahan konfigurasi itu sendiri: Dalam hal ini Loader dapat diinisialisasi di onCreate tetapi kita perlu secara manual mempertahankan id loader atau kita tidak akan dapat membuat panggilan initLoader / restartLoader yang diperlukan. Jika id disimpan dalam ArrayList, kita akan melakukan
outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)
in onSaveInstanceState dan mengembalikan id di onCreate:
loaderIdsArray =
savedInstanceState.getIntegerArrayList(loaderIdsKey)
sebelum kita membuat panggilan initLoader.
initLoader
(dan semua panggilan balik telah selesai, Loader menganggur) setelah rotasi Anda tidak akan mendapatkanonLoadFinished
panggilan balik tetapi jika Anda menggunakanrestartLoader
Anda akan?