page.title=Aktivitas parent.title=Loader parent.link=activities.html @jd:body
Diperkenalkan di Android 3.0, loader memudahkan pemuatan data asinkron dalam aktivitas atau fragmen. Loader memiliki karakteristik ini:
Ada beberapa kelas dan antarmuka yang mungkin dilibatkan dalam menggunakan loader pada aplikasi. Semuanya dirangkum dalam tabel ini:
Kelas/Antarmuka | Keterangan |
---|---|
{@link android.app.LoaderManager} | Kelas abstrak yang dikaitkan dengan {@link android.app.Activity} atau
{@link android.app.Fragment} untuk mengelola satu atau beberapa instance {@link
android.content.Loader}. Ini membantu aplikasi mengelola
operasi berjalan lebih lama bersamaan dengan daur hidup {@link android.app.Activity}
atau {@link android.app.Fragment}; penggunaan paling umumnya adalah dengan
{@link android.content.CursorLoader}, akan tetapi aplikasi bebas menulis loader-nya
sendiri untuk memuat tipe data lainnya.
Hanya ada satu {@link android.app.LoaderManager} per aktivitas atau fragmen. Namun {@link android.app.LoaderManager} bisa memiliki beberapa loader. |
{@link android.app.LoaderManager.LoaderCallbacks} | Antarmuka callback untuk klien berinteraksi dengan {@link android.app.LoaderManager}. Misalnya, Anda menggunakan metode callback {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} untuk membuat loader baru. |
{@link android.content.Loader} | Kelas abstrak yang melakukan pemuatan data asinkron. Ini adalah kelas dasar untuk loader. Biasanya Anda akan menggunakan {@link android.content.CursorLoader}, namun Anda bisa menerapkan subkelas sendiri. Selagi loader aktif, loader harus memantau sumber datanya dan memberikan hasil baru bila konten berubah. |
{@link android.content.AsyncTaskLoader} | Loader abstrak yang menyediakan {@link android.os.AsyncTask} untuk melakukan pekerjaan. |
{@link android.content.CursorLoader} | Subkelas {@link android.content.AsyncTaskLoader} yang meng-query {@link android.content.ContentResolver} dan mengembalikan {@link android.database.Cursor}. Kelas ini mengimplementasikan protokol {@link android.content.Loader} dengan cara standar untuk query kursor, yang dibuat berdasarkan {@link android.content.AsyncTaskLoader} untuk melakukan query kursor pada thread latar belakang agar tidak memblokir UI aplikasi. Menggunakan loader ini merupakan cara terbaik untuk memuat data secara asinkron dari {@link android.content.ContentProvider}, sebagai ganti melakukan query terkelola melalui fragmen atau API aktivitas. |
Kelas dan antarmuka dalam tabel di atas merupakan komponen esensial yang akan Anda gunakan untuk mengimplementasikan loader dalam aplikasi Anda. Anda tidak memerlukan semuanya untuk setiap loader yang dibuat, namun Anda akan selalu memerlukan acuan ke {@link android.app.LoaderManager} untuk memulai loader dan implementasi kelas {@link android.content.Loader} seperti {@link android.content.CursorLoader}. Bagian berikut ini menunjukkan kepada Anda cara menggunakan kelas dan antarmuka ini dalam aplikasi.
Bagian ini menjelaskan cara menggunakan loader dalam aplikasi Android. Aplikasi yang menggunakan loader biasanya berisi yang berikut ini:
{@link android.app.LoaderManager} mengelola satu atau beberapa instance {@link android.content.Loader} dalam {@link android.app.Activity} atau {@link android.app.Fragment}. Hanya ada satu {@link android.app.LoaderManager} per aktivitas atau fragmen.
Anda biasanya memulai {@link android.content.Loader} dalam metode {@link android.app.Activity#onCreate onCreate()} aktivitas, atau dalam metode {@link android.app.Fragment#onActivityCreated onActivityCreated()} fragmen. Anda melakukannya dengan cara berikut ini:
// Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this);
Metode {@link android.app.LoaderManager#initLoader initLoader()} mengambil parameter berikut:
null
).Panggilan {@link android.app.LoaderManager#initLoader initLoader()} memastikan bahwa loader telah dimulai dan aktif. Ia memiliki dua kemungkinan hasil:
Dalam hal ini, implementasi {@link android.app.LoaderManager.LoaderCallbacks} yang ditentukan akan dikaitkan dengan loader, dan akan dipanggil bila status loader berubah. Jika saat panggilan ini status pemanggil sudah dimulai, dan loader yang diminta sudah ada dan telah menghasilkan datanya, maka sistem segera memanggil {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} (selama {@link android.app.LoaderManager#initLoader initLoader()}), sehingga Anda harus siap bila hal ini terjadi. Lihat onLoadFinished untuk diskusi selengkapnya mengenai callback ini
Perhatikan bahwa metode {@link android.app.LoaderManager#initLoader initLoader()} mengembalikan {@link android.content.Loader} yang dibuat, namun Anda tidak perlu menangkap acuan ke sana. {@link android.app.LoaderManager} mengelola masa hidup loader secara otomatis. {@link android.app.LoaderManager} memulai dan menghentikan pemuatan jika perlu, dan menjaga status loader dan konten terkaitnya. Seperti yang tersirat di sini, Anda akan jarang berinteraksi dengan loader secara langsung (meskipun misalnya menggunakan metode loader untuk menyempurnakan perilaku loader, lihat contoh LoaderThrottle). Anda paling sering akan menggunakan metode {@link android.app.LoaderManager.LoaderCallbacks} untuk mengintervensi proses pemuatan saat terjadi kejadian tertentu. Untuk diskusi selengkapnya mengenai topik ini, lihat Menggunakan Callback LoaderManager.
Bila Anda menggunakan {@link android.app.LoaderManager#initLoader initLoader()}, seperti ditampilkan di atas, loader yang ada akan digunakan dengan ID yang ditetapkan jika ada. Jika tidak ada, ID akan dibuat. Namun kadang-kadang Anda perlu membuang data lama dan mulai dari awal.
Untuk membuang data lama, gunakan {@link android.app.LoaderManager#restartLoader restartLoader()}. Misalnya, implementasi {@link android.widget.SearchView.OnQueryTextListener} ini akan me-restart bila query pengguna berubah. Loader perlu di-restart agar dapat menggunakan filter pencarian yang telah direvisi untuk melakukan query baru:
public boolean onQueryTextChanged(String newText) { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; getLoaderManager().restartLoader(0, null, this); return true; }
{@link android.app.LoaderManager.LoaderCallbacks} adalah antarmuka callback yang memungkinkan klien berinteraksi dengan {@link android.app.LoaderManager}.
Loader, khususnya {@link android.content.CursorLoader}, diharapkan mempertahankan datanya setelah dihentikan. Ini memungkinkan aplikasi mempertahankan datanya di aktivitas atau metode {@link android.app.Activity#onStop onStop()} fragmen dan {@link android.app.Activity#onStart onStart()}, sehingga bila pengguna kembali ke aplikasi, mereka tidak harus menunggu data dimuat kembali. Anda menggunakan metode {@link android.app.LoaderManager.LoaderCallbacks} untuk mengetahui waktu membuat loader baru, dan memberi tahu aplikasi kapan berhenti menggunakan data loader.
{@link android.app.LoaderManager.LoaderCallbacks} berisi metode ini:
Metode ini dijelaskan lebih detail dalam bagian berikutnya.
Saat Anda mencoba mengakses loader (misalnya, melalui {@link android.app.LoaderManager#initLoader initLoader()}), ia akan memeriksa untuk mengetahui adanya loader yang ditetapkan oleh ID. Jika tidak ada, ia akan memicu metode {@link android.app.LoaderManager.LoaderCallbacks} {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. Di sinilah Anda membuat loader baru. Biasanya ini adalah {@link android.content.CursorLoader}, namun Anda bisa mengimplementasikan sendiri subkelas {@link android.content.Loader}.
Dalam contoh ini, metode callback {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} akan membuat {@link android.content.CursorLoader}. Anda harus membuat {@link android.content.CursorLoader} menggunakan metode konstruktornya, yang memerlukan set informasi lengkap untuk melakukan query ke {@link android.content.ContentProvider}. Secara khusus, ia memerlukan:
null
akan mengembalikan semua kolom, jadi tidak efisien. null
akan mengembalikan semua baris untuk URI yang diberikan. null
akan
menggunakan urutan sortir default, yang mungkin tidak berurutan.Misalnya:
// If non-null, this is the current filter the user has provided. String mCurFilter; ... public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (mCurFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); }
Metode ini dipanggil bila loader yang dibuat sebelumnya selesai dimuat. Metode ini dijamin dipanggil sebelum pelepasan data terakhir yang disediakan untuk loader ini. Di titik ini Anda harus menyingkirkan semua penggunaan data lama (karena akan segera dilepas), namun jangan melepas sendiri data tersebut karena loader memilikinya dan akan menanganinya.
Loader akan melepas data setelah mengetahui bahwa aplikasi tidak lagi menggunakannya. Misalnya, jika data adalah kursor dari {@link android.content.CursorLoader}, Anda tidak boleh memanggil {@link android.database.Cursor#close close()} sendiri. Jika kursor ditempatkan dalam {@link android.widget.CursorAdapter}, Anda harus menggunakan metode {@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()} agar {@link android.database.Cursor} lama tidak ditutup. Misalnya:
// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter; ... public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); }
Metode ini dipanggil bila loader yang dibuat sebelumnya sedang di-reset, sehingga datanya tidak tersedia. Callback ini memungkinkan Anda mengetahui kapan data akan dilepas sehingga dapat menghapus acuannya ke callback.
Implementasi ini memanggil
{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}
dengan nilai null
:
// This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; ... public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); }
Sebagai contoh, berikut ini adalah implementasi penuh {@link android.app.Fragment} yang menampilkan {@link android.widget.ListView} berisi hasil query terhadap penyedia konten kontak. Ia menggunakan {@link android.content.CursorLoader} untuk mengelola query pada penyedia.
Agar aplikasi dapat mengakses kontak pengguna, seperti yang ditampilkan dalam contoh ini, manifesnya harus menyertakan izin {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.
public static class CursorLoaderListFragment extends ListFragment implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { // This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; // If non-null, this is the current filter the user has provided. String mCurFilter; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Give some text to display if there is no data. In a real // application this would come from a resource. setEmptyText("No phone numbers"); // We have a menu item to show in action bar. setHasOptionsMenu(true); // Create an empty adapter we will use to display the loaded data. mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_2, null, new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, new int[] { android.R.id.text1, android.R.id.text2 }, 0); setListAdapter(mAdapter); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Place an action bar item for searching. MenuItem item = menu.add("Search"); item.setIcon(android.R.drawable.ic_menu_search); item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); SearchView sv = new SearchView(getActivity()); sv.setOnQueryTextListener(this); item.setActionView(sv); } public boolean onQueryTextChange(String newText) { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; getLoaderManager().restartLoader(0, null, this); return true; } @Override public boolean onQueryTextSubmit(String query) { // Don't care about this. return true; } @Override public void onListItemClick(ListView l, View v, int position, long id) { // Insert desired behavior here. Log.i("FragmentComplexList", "Item clicked: " + id); } // These are the Contacts rows that we will retrieve. static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, }; public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (mCurFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); } public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); } }
Ada beberapa contoh berbeda dalam ApiDemos yang mengilustrasikan cara menggunakan loader:
Untuk informasi tentang mengunduh dan menginstal contoh SDK, lihat Mendapatkan Contoh.