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?Memanggil
initLoader
ketika Loader sudah dibuat (ini biasanya terjadi setelah perubahan konfigurasi, misalnya) memberitahu LoaderManager untuk mengirimkan data terbaru Loader keonLoadFinished
segera. Jika Loader belum dibuat (ketika aktivitas / fragmen pertama kali diluncurkan, misalnya) panggilan untukinitLoader
memberi tahu LoaderManager untuk memanggilonCreateLoader
untuk membuat Loader baru.Memanggil
restartLoader
menghancurkan Loader yang sudah ada (serta data yang ada yang terkait dengannya) dan memberi tahu LoaderManager untuk memanggilonCreateLoader
untuk membuat Loader baru dan untuk memulai load baru.Dokumentasi juga cukup jelas tentang ini:
initLoader
memastikan Loader diinisialisasi dan aktif. Jika loader belum ada, dibuat dan (jika aktivitas / fragmen dimulai) memulai loader. Kalau tidak, loader yang dibuat terakhir digunakan kembali.restartLoader
memulai yang baru atau me-restart Loader yang ada di manajer ini, mendaftarkan panggilan balik ke sana, dan (jika aktivitas / fragmen sedang dimulai) mulai memuatnya. Jika pemuat dengan id yang sama sebelumnya telah dimulai, pemuat itu akan secara otomatis dihancurkan ketika pemuat baru menyelesaikan pekerjaannya. Callback akan dikirim sebelum loader lama dihancurkan.sumber
initLoader()
dionCreate()
/onActivityCreated()
saat aktivitas / fragmen pertama kali dimulai. Dengan cara ini, ketika pengguna pertama kali membuka suatu kegiatan, loader akan dibuat untuk pertama kalinya ... tetapi pada setiap perubahan konfigurasi berikutnya di mana seluruh aktivitas / fragmen harus dihancurkan, panggilan berikut untukinitLoader()
hanya akan mengembalikan yang lamaLoader
alih - alih membuat yang baru. Biasanya Anda gunakanrestartLoader()
ketika Anda perlu mengubahLoader
kueri (yaitu Anda ingin mendapatkan data yang difilter / diurutkan, dll.).Baru-baru ini saya menemukan masalah dengan beberapa manajer pemuat dan perubahan orientasi layar dan ingin mengatakan bahwa setelah banyak coba-coba, pola berikut ini berfungsi untuk saya di Aktivitas dan Fragmen:
(dengan kata lain, atur beberapa flag sehingga initLoader adalah selalu dijalankan sekali & yang restartLoader dijalankan pada 2 & selanjutnya lewat melalui onResume )
Juga, ingatlah untuk menetapkan id yang berbeda untuk masing-masing loader Anda dalam suatu Kegiatan (yang bisa sedikit masalah dengan fragmen dalam aktivitas itu jika Anda tidak berhati-hati dengan penomoran Anda)
Saya mencoba menggunakan initLoader saja .... sepertinya tidak bekerja secara efektif.
Mencoba initLoader di onCreate dengan null args (docs bilang ini ok) & restartLoader (dengan args yang valid) di onResume .... docs salah & initLoader melempar pengecualian nullpointer.
Mencoba me-restartLoader saja ... berfungsi untuk sementara waktu tetapi gagal pada orientasi ulang layar ke-5 atau ke-6.
Mencoba initLoader di onResume ; lagi bekerja sebentar & kemudian berhembus. (khusus "Called doRetain when not start:" ... error)
Mencoba yang berikut: (kutipan dari kelas sampul yang memiliki id loader dilewatkan ke konstruktor)
(yang saya temukan di suatu tempat di Stack-Overflow)
Sekali lagi, ini bekerja untuk sementara waktu tetapi sesekali masih menimbulkan kesalahan.
Dari apa yang saya tahu saat debugging, saya pikir ada sesuatu yang harus dilakukan dengan save / restore keadaan instance yang mengharuskan initLoader (/ s) dijalankan di onCreate dari siklus hidup jika mereka ingin bertahan dari putaran siklus . (Saya mungkin salah.)
dalam kasus Manajer yang tidak dapat dimulai sampai hasilnya kembali dari manajer atau tugas lain (mis. tidak dapat diinisialisasi di onCreate ), saya hanya menggunakan initLoader . (Saya mungkin tidak benar dalam hal ini tetapi tampaknya berfungsi. Loader sekunder ini bukan bagian dari kondisi instan sehingga menggunakan initLoader mungkin benar dalam kasus ini)
Melihat diagram dan dokumen, saya akan berpikir bahwa initLoader harus masuk onCreate & restartLoader di onRestart untuk Kegiatan tetapi meninggalkan Fragmen menggunakan beberapa pola yang berbeda dan saya tidak punya waktu untuk menyelidiki apakah ini benar-benar stabil. Adakah yang bisa mengomentari orang lain jika mereka sukses dengan pola kegiatan ini?
sumber
initLoader
akan menggunakan kembali parameter yang sama jika loader sudah ada. Itu segera kembali jika data lama sudah dimuat, bahkan jika Anda menyebutnya dengan parameter baru. Loader idealnya secara otomatis memberitahukan aktivitas data baru. Jika layar diputar,initLoader
akan dipanggil lagi dan data lama akan segera ditampilkan.restartLoader
adalah untuk saat Anda ingin memaksa memuat ulang dan mengubah parameter juga. Jika Anda membuat layar masuk menggunakan loader, Anda hanya akan meneleponrestartLoader
setiap kali tombol diklik. (Tombol dapat diklik beberapa kali karena kredensial yang salah, dll.). Anda hanya akan meneleponinitLoader
saat memulihkan keadaan instance tersimpan aktivitas jika layar diputar saat login sedang berlangsung.sumber
Jika loader sudah ada, restartLoader akan menghentikan / membatalkan / menghancurkan yang lama, sementara initLoader hanya akan menginisialisasi dengan panggilan balik yang diberikan. Saya tidak bisa mengetahui apa yang dilakukan callback lama dalam kasus ini, tapi saya kira mereka akan ditinggalkan begitu saja.
Saya memindai melalui http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/app/LoaderManager.java tetapi saya tidak dapat menemukan apa persisnya Perbedaannya adalah, terlepas dari itu metode melakukan hal yang berbeda. Jadi saya akan mengatakan, gunakan initLoader pertama kali dan restart untuk kali berikut, meskipun saya tidak bisa mengatakan dengan pasti apa yang masing-masing dari mereka akan lakukan dengan tepat.
sumber
initLoader
dilakukan dalam kasus ini?Loader init pada start pertama menggunakan metode loadInBackground (), pada start kedua akan dihilangkan. Jadi, menurut saya, solusi yang lebih baik adalah:
//////////////////////////////////////////////////// ///////////////////////////
Saya menghabiskan banyak waktu untuk menemukan solusi ini - restartLoader (...) tidak berfungsi dengan baik dalam kasus saya. Satu-satunya forceLoad () memungkinkan untuk menyelesaikan utas pemuatan sebelumnya tanpa panggilan balik (sehingga Anda akan menyelesaikan semua transaksi db dengan benar) dan memulai kembali utas baru. Ya, ini membutuhkan waktu ekstra, tetapi lebih stabil. Hanya utas mulai terakhir yang akan menerima panggilan balik. Jadi, jika Anda ingin melakukan tes dengan mengganggu transaksi db Anda - selamat datang, cobalah untuk me-restartLoader (...), jika tidak, forceLoad (). Satu-satunya kemudahan restartLoader (...) adalah untuk memberikan data awal baru, maksud saya parameter. Dan jangan lupa untuk menghancurkan loader di metode onDetach () dari Fragment yang sesuai dalam kasus ini. Juga perlu diingat, bahwa beberapa kali, ketika Anda memiliki aktivitas dan, biarkan mereka berkata, 2 fragmen dengan Loader, masing-masing aktivitas inklusif - Anda hanya akan mencapai 2 Manajer Loader, jadi Activity membagikan LoaderManager-nya dengan Fragmen, yang diperlihatkan di layar terlebih dahulu saat memuat. Coba LoaderManager.enableDebugLogging (true); untuk melihat detail dalam setiap kasus tertentu.
sumber
getLoader(0)
dalamtry { ... } catch (Exception e) { ... }
.