Loader

Loader tidak digunakan lagi di Android P (API 28). Opsi yang disarankan untuk menangani pemuatan data saat menangani siklus hidup Aktivitas dan Fragmen adalah menggunakan kombinasi ViewModels dan LiveData. ViewModels bertahan dari perubahan konfigurasi seperti Loader tetapi dengan lebih sedikit boilerplate. LiveData memberikan cara lifecycle-aware pemuatan data yang bisa Anda gunakan kembali dalam beberapa ViewModels. Anda juga bisa menggabungkan LiveData menggunakan MediatorLiveData, dan kueri yang dapat diamati, seperti kueri dari Room database, bisa digunakan untuk mengamati perubahan pada data. ViewModels dan LiveData juga tersedia pada situasi saat Anda tidak memiliki akses ke LoaderManager, seperti dalam Service. Menggunakan ViewModels dan liveData secara bersamaan memberikan cara mudah untuk mengakses data yang diperlukan aplikasi Anda tanpa harus berurusan dengan siklus hidup UI. Untuk mempelajari selengkapnya tentang LiveData lihat panduan LiveData dan untuk mempelajari selengkapnya tentang ViewModels lihat panduan ViewModel.

Loader API memungkinkan Anda memuat data dari penyedia konten atau dari sumber data lain untuk tampilan dalam FragmentActivity atau Fragment. Jika Anda tidak memahami mengapa Anda memerlukan Loader API untuk melakukan operasi yang tampaknya sepele, maka pertama-tama pertimbangkan beberapa masalah yang dapat Anda alami tanpa loader:

  • Jika Anda mengambil data secara langsung dalam aktivitas atau fragmen, pengguna Anda akan kesulitan karena kurangnya responsif, karena melakukan kueri yang berpotensi lambat dari thread UI.
  • Jika Anda mengambil data dari thread lain, mungkin dengan AsyncTask, maka Anda bertanggung jawab untuk mengelola thread dan thread UI melalui berbagai kejadian siklus hidup aktivitas atau fragmen, seperti onDestroy() dan perubahan konfigurasi.

Loader memecahkan semua masalah ini dan menyertakan manfaat lain. Sebagai contoh:

  • Loader berjalan di thread terpisah untuk mencegah UI tidak responsif atau tersendat.
  • Loader menyederhanakan pengelolaan thread dengan menyediakan metode callback bila terjadi peristiwa.
  • Loader mempertahankan dan membuat cache hasil ketika konfigurasi berubah untuk mencegah duplikasi kueri.
  • Loader bisa mengimplementasikan pengamat untuk memantau perubahan dalam sumber data dasarnya. Misalnya, CursorLoader secara otomatis mendaftarkan ContentObserver untuk memicu muat ulang saat data berubah.

Rangkuman Loader API

Ada beberapa kelas dan antarmuka yang mungkin dilibatkan dalam menggunakan loader pada aplikasi. Semuanya dirangkum dalam tabel ini:

Kelas/Antarmuka Keterangan
LoaderManager Kelas abstrak yang dikaitkan dengan FragmentActivity atau Fragment untuk mengelola satu atau beberapa instance Loader. Hanya ada satu LoaderManager per aktivitas atau fragmen, tetapi LoaderManager bisa mengelola beberapa loader.

Untuk mendapatkan LoaderManager, panggil getSupportLoaderManager() dari aktivitas atau fragmen.

Untuk memulai pemuatan data dari loader, panggil initLoader() atau restartLoader(). Sistem akan secara otomatis menentukan apakah loader dengan ID integer yang sama ada dan akan membuat loader baru atau menggunakan kembali loader yang sudah ada.

LoaderManager.LoaderCallbacks Antarmuka ini berisi metode callback yang dipanggil saat terjadi peristiwa loader. Antarmuka menetapkan tiga metode callback:
  • onCreateLoader(int, Bundle) - dipanggil saat sistem memerlukan loader baru yang harus dibuat. Kode Anda harus membuat objek Loader dan mengembalikannya ke sistem.
  • onLoadFinished(Loader<D>, D) - dipanggil saat loader telah menyelesaikan pemuatan data. Biasanya, kode Anda harus menampilkan data kepada pengguna.
  • onLoaderReset(Loader<D>) - dipanggil saat loader yang dibuat sebelumnya sedang disetel ulang (saat Anda memanggil destroyLoader(int) atau saat aktivitas atau fragmen dihapus, sehingga datanya tidak tersedia. Kode Anda harus membuang semua referensi apa pun yang dimilikinya ke data loader.
Antarmuka biasanya diimplementasikan oleh aktivitas atau fragmen Anda dan didaftarkan saat Anda memanggil initLoader() atau restartLoader().
Loader Loader melakukan pemuatan data. Kelas ini abstrak dan berfungsi sebagai kelas dasar untuk semua loader. Anda bisa secara langsung menjadikan subkelas Loader atau menggunakan salah satu subkelas bawaan berikut untuk memudahkan implementasi:

Bagian berikut ini menunjukkan kepada Anda cara menggunakan class dan antarmuka ini dalam aplikasi.

Menggunakan Loader dalam Aplikasi

Bagian ini menjelaskan cara menggunakan loader dalam aplikasi Android. Aplikasi yang menggunakan loader biasanya menyertakan yang berikut ini:

Memulai Loader

LoaderManager mengelola satu atau beberapa instance Loader dalam FragmentActivity atau Fragment. Hanya ada satu LoaderManager per aktivitas atau fragmen.

Anda biasanya melakukan inisialisasi Loader dalam metode onCreate() aktivitas, atau dalam metode onActivityCreated() fragmen. Anda melakukannya dengan cara berikut ini:

Kotlin

supportLoaderManager.initLoader(0, null, this)

Java

// Prepare the loader.  Either re-connect with an existing one,
// or start a new one.
getSupportLoaderManager().initLoader(0, null, this);

Metode initLoader() mengambil parameter berikut:

  • ID unik yang mengidentifikasi loader. Dalam contoh ini, ID-nya adalah 0.
  • Argumen opsional untuk dipasok ke loader pada saat pembuatan (dalam contoh ini null).
  • Implementasi LoaderManager.LoaderCallbacks, yang akan dipanggil LoaderManager untuk melaporkan kejadian loader. Dalam contoh ini, kelas lokal mengimplementasikan antarmuka LoaderManager.LoaderCallbacks, sehingga meneruskan referensi ke dirinya sendiri, this.

Panggilan initLoader() memastikan bahwa loader telah dimulai dan aktif. Ia memiliki dua kemungkinan hasil:

  • Jika loader yang disebutkan oleh ID sudah ada, loader yang dibuat terakhir akan digunakan kembali.
  • Jika loader yang ditentukan melalui ID tidak ada, initLoader() akan memicu metode LoaderManager.LoaderCallbacks metode onCreateLoader(). Di sinilah Anda mengimplementasikan kode untuk membuat instance dan mengembalikan loader baru. Untuk pembahasan selengkapnya, lihat bagian onCreateLoader.

Dalam hal ini, implementasi LoaderManager.LoaderCallbacks yang diberikan 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 onLoadFinished() (selama initLoader()), sehingga Anda harus siap bila hal ini terjadi. Lihat onLoadFinished untuk pembahasan selengkapnya mengenai callback ini

Perhatikan bahwa metode initLoader() mengembalikan Loader yang dibuat, namun Anda tidak perlu menangkap acuan ke sana. LoaderManager mengelola masa hidup loader secara otomatis. LoaderManager memulai dan menghentikan pemuatan jika perlu, dan menjaga status loader dan materi 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 LoaderManager.LoaderCallbacks untuk mengintervensi proses pemuatan saat terjadi kejadian tertentu. Untuk diskusi selengkapnya mengenai topik ini, lihat Menggunakan Callback LoaderManager.

Memulai Ulang Loader

Bila Anda menggunakan 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 restartLoader(). Misalnya, implementasi SearchView.OnQueryTextListener ini akan memulai ulang loader bila kueri pengguna berubah. Loader perlu dimulai ulang agar dapat menggunakan filter penelusuran yang telah direvisi untuk melakukan kueri baru:

Kotlin

fun onQueryTextChanged(newText: String?): Boolean {
    // 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.
    curFilter = if (newText?.isNotEmpty() == true) newText else null
    supportLoaderManager.restartLoader(0, null, this)
    return true
}

Java

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.
    curFilter = !TextUtils.isEmpty(newText) ? newText : null;
    getSupportLoaderManager().restartLoader(0, null, this);
    return true;
}

Menggunakan Callback LoaderManager

LoaderManager.LoaderCallbacks adalah antarmuka callback yang memungkinkan klien berinteraksi dengan LoaderManager.

Loader, khususnya CursorLoader, diharapkan mempertahankan datanya setelah dihentikan. Ini memungkinkan aplikasi mempertahankan datanya di aktivitas atau metode onStop() dan onStart() fragmen, sehingga bila pengguna kembali ke aplikasi, mereka tidak harus menunggu data dimuat kembali. Anda menggunakan metode LoaderManager.LoaderCallbacks untuk mengetahui waktu membuat loader baru, dan memberi tahu aplikasi kapan berhenti menggunakan data loader.

LoaderManager.LoaderCallbacks berisi metode ini:

  • onLoadFinished() — Dipanggil bila loader yang dibuat sebelumnya selesai dimuat.
  • onLoaderReset() — Dipanggil bila loader yang dibuat sebelumnya sedang disetel ulang, sehingga datanya tidak tersedia.

Metode ini dijelaskan lebih detail dalam bagian berikutnya.

onCreateLoader

Saat Anda mencoba mengakses loader (misalnya, melalui initLoader()), ia akan memeriksa untuk mengetahui adanya loader yang ditetapkan melalui ID. Jika tidak ada, metode LoaderManager.LoaderCallbacks onCreateLoader() akan dipicu. Di sinilah Anda membuat loader baru. Biasanya ini berupa CursorLoader, namun Anda bisa mengimplementasikan subkelas Loader sendiri.

Dalam contoh ini, metode callback onCreateLoader() membuat CursorLoader. Anda harus membuat CursorLoader menggunakan metode konstruktornya, yang memerlukan set informasi lengkap untuk melakukan kueri ke ContentProvider. Khususnya, ia memerlukan:

  • uri — URI untuk materi yang akan diambil.
  • projection — Daftar berisi kolom yang akan dikembalikan. Meneruskan null akan mengembalikan semua kolom, jadi tidak efisien.
  • selection — Filter yang mendeklarasikan baris yang akan dikembalikan, diformat sebagai klausa SQL WHERE (tidak termasuk WHERE itu sendiri). Meneruskan null akan mengembalikan semua baris untuk URI yang diberikan.
  • selectionArgs — Anda dapat menyertakan ?s dalam pilihan, yang akan digantikan dengan nilai dari selectionArgs, agar muncul dalam pilihan. Nilai-nilai akan diikat sebagai String.
  • sortOrder — Cara menyusun baris, diformat sebagai klausa ORDER BY dari SQL (tidak termasuk ORDER BY itu sendiri). Meneruskan null akan menggunakan urutan sortir default, yang mungkin tidak berurutan.

Sebagai contoh:

Kotlin

// If non-null, this is the current filter the user has provided.
private var curFilter: String? = null
...
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
    // 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.
    val baseUri: Uri = if (curFilter != null) {
        Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, Uri.encode(curFilter))
    } else {
        ContactsContract.Contacts.CONTENT_URI
    }

    // Now create and return a CursorLoader that will take care of
    // creating a Cursor for the data being displayed.
    val select: String = "((${Contacts.DISPLAY_NAME} NOTNULL) AND (" +
            "${Contacts.HAS_PHONE_NUMBER}=1) AND (" +
            "${Contacts.DISPLAY_NAME} != ''))"
    return (activity as? Context)?.let { context ->
        CursorLoader(
                context,
                baseUri,
                CONTACTS_SUMMARY_PROJECTION,
                select,
                null,
                "${Contacts.DISPLAY_NAME} COLLATE LOCALIZED ASC"
        )
    } ?: throw Exception("Activity cannot be null")
}

Java

// If non-null, this is the current filter the user has provided.
String curFilter;
...
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 (curFilter != null) {
        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(curFilter));
    } 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");
}

onLoadFinished

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 membuang 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 CursorLoader, Anda tidak boleh memanggil close() pada dirinya sendiri. Jika kursor ditempatkan dalam CursorAdapter, Anda harus menggunakan metode swapCursor() agar Cursor lama tidak ditutup. Sebagai contoh:

Kotlin

private lateinit var adapter: SimpleCursorAdapter
...

override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) {
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    adapter.swapCursor(data)
}

Java

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter adapter;
...

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.)
    adapter.swapCursor(data);
}

onLoaderReset

Metode ini dipanggil bila loader yang dibuat sebelumnya sedang disetel ulang, sehingga datanya tidak tersedia. Callback ini memungkinkan Anda mengetahui kapan data akan dilepas sehingga dapat membuang acuannya ke callback.  

Implementasi ini memanggil swapCursor() dengan nilai null:

Kotlin

private lateinit var adapter: SimpleCursorAdapter
...

override fun onLoaderReset(loader: Loader<Cursor>) {
    // 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.
    adapter.swapCursor(null)
}

Java

// This is the Adapter being used to display the list's data.
SimpleCursorAdapter adapter;
...

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.
    adapter.swapCursor(null);
}

Contoh

Sebagai contoh, inilah implementasi penuh Fragment yang menampilkan ListView berisi hasil kueri terhadap penyedia materi kontak. CursorLoader digunakan untuk mengelola kueri pada penyedia.

Agar aplikasi dapat mengakses kontak pengguna, seperti yang ditampilkan dalam contoh ini, manifesnya harus menyertakan izin READ_CONTACTS.

Kotlin

private val CONTACTS_SUMMARY_PROJECTION: Array<String> = arrayOf(
        Contacts._ID,
        Contacts.DISPLAY_NAME,
        Contacts.CONTACT_STATUS,
        Contacts.CONTACT_PRESENCE,
        Contacts.PHOTO_ID,
        Contacts.LOOKUP_KEY
)


class CursorLoaderListFragment :
        ListFragment(),
        SearchView.OnQueryTextListener,
        LoaderManager.LoaderCallbacks<Cursor> {

    // This is the Adapter being used to display the list's data.
    private lateinit var mAdapter: SimpleCursorAdapter

    // If non-null, this is the current filter the user has provided.
    private var curFilter: String? = null

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        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 = SimpleCursorAdapter(activity,
                android.R.layout.simple_list_item_2,
                null,
                arrayOf(Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS),
                intArrayOf(android.R.id.text1, android.R.id.text2),
                0
        )
        listAdapter = mAdapter

        // Prepare the loader.  Either re-connect with an existing one,
        // or start a new one.
        loaderManager.initLoader(0, null, this)
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        // Place an action bar item for searching.
        menu.add("Search").apply {
            setIcon(android.R.drawable.ic_menu_search)
            setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
            actionView = SearchView(activity).apply {
                setOnQueryTextListener(this@CursorLoaderListFragment)
            }
        }
    }

    override fun onQueryTextChange(newText: String?): Boolean {
        // 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.
        curFilter = if (newText?.isNotEmpty() == true) newText else null
        loaderManager.restartLoader(0, null, this)
        return true
    }

    override fun onQueryTextSubmit(query: String): Boolean {
        // Don't care about this.
        return true
    }

    override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) {
        // Insert desired behavior here.
        Log.i("FragmentComplexList", "Item clicked: $id")
    }

    override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
        // 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.
        val baseUri: Uri = if (curFilter != null) {
            Uri.withAppendedPath(Contacts.CONTENT_URI, Uri.encode(curFilter))
        } else {
            Contacts.CONTENT_URI
        }

        // Now create and return a CursorLoader that will take care of
        // creating a Cursor for the data being displayed.
        val select: String = "((${Contacts.DISPLAY_NAME} NOTNULL) AND (" +
                "${Contacts.HAS_PHONE_NUMBER}=1) AND (" +
                "${Contacts.DISPLAY_NAME} != ''))"
        return (activity as? Context)?.let { context ->
            CursorLoader(
                    context,
                    baseUri,
                    CONTACTS_SUMMARY_PROJECTION,
                    select,
                    null,
                    "${Contacts.DISPLAY_NAME} COLLATE LOCALIZED ASC"
            )
        } ?: throw Exception("Activity cannot be null")
    }

    override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
        // Swap the new cursor in.  (The framework will take care of closing the
        // old cursor once we return.)
        mAdapter.swapCursor(data)
    }

    override fun onLoaderReset(loader: Loader<Cursor>) {
        // 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)
    }
}

Java

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 curFilter;

    @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.
        curFilter = !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 (curFilter != null) {
            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                    Uri.encode(curFilter));
        } 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);
    }
}

Contoh Selengkapnya

Contoh berikut ini mengilustrasikan cara menggunakan loader: