Ladeprogramme

Seit Android 9 (API-Level 28) werden Loader nicht mehr unterstützt. Wenn Sie sich mit dem Laden von Daten bei der Verarbeitung der Lebenszyklen Activity und Fragment befassen möchten, empfehlen wir, eine Kombination aus ViewModel-Objekten und LiveData zu verwenden. Sie können Modelle auch nach Konfigurationsänderungen wie Loadern, jedoch mit weniger Boilerplate-Code ansehen. LiveData bietet eine lebenszyklusbasierte Methode zum Laden von Daten, die Sie in Modellen für mehrere Ansichten wiederverwenden können. Sie können LiveData auch mit MediatorLiveData kombinieren. Mit allen beobachtbaren Abfragen, z. B. aus einer Raumdatenbank, können Änderungen an den Daten beobachtet werden.

ViewModel und LiveData sind auch in Situationen verfügbar, in denen du keinen Zugriff auf LoaderManager hast, z. B. in Service. Die Kombination aus beiden bietet eine einfache Möglichkeit, auf die Daten zuzugreifen, die Ihre App benötigt, ohne sich mit dem UI-Lebenszyklus befassen zu müssen. Weitere Informationen zu LiveData finden Sie in der Übersicht zu LiveData. Weitere Informationen zu ViewModel finden Sie in der Übersicht zu ViewModel.

Mit der Loader API können Sie Daten aus einem Contentanbieter oder einer anderen Datenquelle zur Anzeige in einem FragmentActivity- oder Fragment-Objekt laden.

Ohne Loader können unter anderem folgende Probleme auftreten:

  • Wenn Sie die Daten direkt in der Aktivität oder dem Fragment abrufen, reagieren Ihre Nutzer möglicherweise nicht richtig, da sie möglicherweise langsame Abfragen über den UI-Thread ausführen.
  • Wenn Sie die Daten aus einem anderen Thread abrufen, z. B. mit AsyncTask, sind Sie dafür verantwortlich, sowohl diesen Thread als auch den UI-Thread über verschiedene Aktivitäts- oder Fragment-Lebenszyklusereignisse wie onDestroy() und Konfigurationsänderungen zu verwalten.

Loader lösen diese Probleme und bieten weitere Vorteile:

  • Loader werden in separaten Threads ausgeführt, um eine langsame oder nicht reagierende UI zu verhindern.
  • Loader vereinfachen die Thread-Verwaltung, indem sie beim Eintreten von Ereignissen Callback-Methoden bereitstellen.
  • Loader bleiben erhalten und speichern die Ergebnisse über Konfigurationsänderungen hinweg im Cache, um doppelte Abfragen zu vermeiden.
  • Loader können einen Beobachter implementieren, der Änderungen an der zugrunde liegenden Datenquelle überwacht. Beispielsweise registriert CursorLoader automatisch einen ContentObserver, um eine Aktualisierung auszulösen, wenn sich Daten ändert.

Zusammenfassung der Loader API

Bei der Verwendung von Ladeprogrammen in einer Anwendung können mehrere Klassen und Schnittstellen beteiligt sein. Sie sind in der folgenden Tabelle zusammengefasst:

Klasse/Benutzeroberfläche Beschreibung
LoaderManager Eine abstrakte Klasse, die einem FragmentActivity oder Fragment zum Verwalten einer oder mehrerer Loader-Instanzen zugeordnet ist. Es gibt nur eine LoaderManager pro Aktivität oder Fragment, aber eine LoaderManager kann mehrere Loader verwalten.

Um ein LoaderManager zu erhalten, rufen Sie getSupportLoaderManager() aus der Aktivität oder dem Fragment auf.

Rufen Sie entweder initLoader() oder restartLoader() auf, um mit dem Laden von Daten aus einem Ladeprogramm zu beginnen. Das System ermittelt automatisch, ob bereits ein Ladeprogramm mit derselben Ganzzahl-ID vorhanden ist, und erstellt entweder einen neuen Loader oder verwendet einen vorhandenen wieder.

LoaderManager.LoaderCallbacks Diese Schnittstelle enthält Callback-Methoden, die aufgerufen werden, wenn Loader-Ereignisse auftreten. In der Schnittstelle sind drei Callback-Methoden definiert:
  • onCreateLoader(int, Bundle): Wird aufgerufen, wenn das System ein neues Ladeprogramm erstellen muss. Erstellen Sie in Ihrem Code ein Loader-Objekt und geben Sie es an das System zurück.
  • onLoadFinished(Loader<D>, D): Wird aufgerufen, wenn ein Loader das Laden der Daten abgeschlossen hat. In der Regel zeigen Sie die Daten dem Nutzer in Ihrem Code an.
  • onLoaderReset(Loader<D>): Wird aufgerufen, wenn ein zuvor erstelltes Ladeprogramm zurückgesetzt wird, wenn Sie destroyLoader(int) aufrufen oder wenn die Aktivität oder das Fragment gelöscht wird, wodurch seine Daten nicht mehr verfügbar sind. Entfernen Sie in Ihrem Code alle Verweise auf die Daten des Ladeprogramms.
Diese Schnittstelle wird in der Regel durch Ihre Aktivität oder Ihr Fragment implementiert und registriert, wenn Sie initLoader() oder restartLoader() aufrufen.
Loader Loader führen das Laden der Daten durch. Diese Klasse ist abstrakt und dient als Basisklasse für alle Loader. Sie können Loader direkt abgeleitet haben oder eine der folgenden integrierten abgeleiteten Klassen verwenden, um die Implementierung zu vereinfachen:

In den folgenden Abschnitten erfahren Sie, wie Sie diese Klassen und Schnittstellen in einer Anwendung verwenden.

Loader in einer Anwendung verwenden

In diesem Abschnitt wird die Verwendung von Loadern in einer Android-Anwendung beschrieben. Eine Anwendung, die Loader verwendet, umfasst in der Regel Folgendes:

Ladeprogramm starten

Der LoaderManager verwaltet eine oder mehrere Loader-Instanzen innerhalb von FragmentActivity oder Fragment. Es gibt nur ein LoaderManager pro Aktivität oder Fragment.

In der Regel initialisieren Sie Loader in der Methode onCreate() der Aktivität oder in der Methode onCreate() des Fragments. Gehen Sie dazu so vor:

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

Die Methode initLoader() verwendet die folgenden Parameter:

  • Eine eindeutige ID, die das Ladeprogramm identifiziert. In diesem Beispiel lautet die ID 0.
  • Optionale Argumente, die beim Erstellen an das Ladeprogramm übergeben werden sollen (in diesem Beispiel null).
  • Eine LoaderManager.LoaderCallbacks-Implementierung, die von LoaderManager aufgerufen wird, um Ladeereignisse zu melden. In diesem Beispiel implementiert die lokale Klasse die LoaderManager.LoaderCallbacks-Schnittstelle und übergibt einen Verweis auf this.

Der Aufruf initLoader() sorgt dafür, dass ein Ladeprogramm initialisiert und aktiv wird. Dies hat zwei mögliche Ergebnisse:

  • Wenn das mit der ID angegebene Ladeprogramm bereits vorhanden ist, wird das zuletzt erstellte Ladeprogramm wiederverwendet.
  • Wenn das durch die ID angegebene Ladeprogramm nicht vorhanden ist, löst initLoader() die LoaderManager.LoaderCallbacks-Methode onCreateLoader() aus. Hier implementieren Sie den Code, um einen neuen Loader zu instanziieren und zurückzugeben. Weitere Informationen finden Sie im Abschnitt zu onCreateLoader.

In beiden Fällen ist die angegebene LoaderManager.LoaderCallbacks-Implementierung mit dem Ladeprogramm verknüpft und wird aufgerufen, wenn sich der Status des Ladeprogramms ändert. Wenn sich der Aufrufer zum Zeitpunkt dieses Aufrufs im Status „Gestartet“ befindet, das angeforderte Ladeprogramm bereits vorhanden ist und seine Daten generiert hat, ruft das System onLoadFinished() sofort während initLoader() auf. Sie müssen darauf vorbereitet sein. Weitere Informationen zu diesem Callback finden Sie im Abschnitt zu onLoadFinished.

Die Methode initLoader() gibt den erstellten Loader zurück, Sie müssen jedoch keinen Verweis darauf erfassen. LoaderManager verwaltet die Lebensdauer des Ladeprogramms automatisch. Der LoaderManager startet und beendet den Ladevorgang bei Bedarf und behält den Status des Ladeprogramms und des zugehörigen Inhalts bei.

Wie das impliziert, werden Sie selten direkt mit den Loadern interagieren. Sie verwenden am häufigsten die Methoden LoaderManager.LoaderCallbacks, um in den Ladeprozess einzugreifen, wenn bestimmte Ereignisse auftreten. Weitere Informationen zu diesem Thema findest du im Abschnitt LoaderManager-Rückrufe verwenden.

Ladeprogramm neu starten

Wenn Sie initLoader() wie im vorherigen Abschnitt gezeigt verwenden, wird ein vorhandenes Ladeprogramm mit der angegebenen ID verwendet, sofern vorhanden. Falls nicht, wird eins erstellt. Aber manchmal möchten Sie Ihre alten Daten verwerfen und von vorne beginnen.

Wenn Sie Ihre alten Daten verwerfen möchten, verwenden Sie restartLoader(). Mit der folgenden Implementierung von SearchView.OnQueryTextListener wird beispielsweise das Ladeprogramm neu gestartet, wenn sich die Abfrage des Nutzers ändert. Das Ladeprogramm muss neu gestartet werden, damit es mit dem überarbeiteten Suchfilter eine neue Abfrage ausführen kann.

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

LoaderManager-Callbacks verwenden

LoaderManager.LoaderCallbacks ist eine Callback-Schnittstelle, über die ein Client mit der LoaderManager interagieren kann.

Von Loadern, insbesondere CursorLoader, wird erwartet, dass ihre Daten nach dem Beenden beibehalten werden. So behalten Anwendungen ihre Daten über die onStop()- und onStart()-Methoden der Aktivität oder des Fragments, sodass Nutzer nicht warten müssen, bis die Daten neu geladen wurden, wenn sie zu einer Anwendung zurückkehren.

Mit den Methoden LoaderManager.LoaderCallbacks können Sie feststellen, wann ein neues Ladeprogramm erstellt werden soll, und der Anwendung mitteilen, wann die Verwendung der Daten eines Ladeprogramms beendet werden soll.

LoaderManager.LoaderCallbacks enthält diese Methoden:

  • onLoadFinished(): Wird aufgerufen, wenn ein zuvor erstelltes Ladeprogramm das Laden abgeschlossen hat.
  • onLoaderReset(): Wird aufgerufen, wenn ein zuvor erstelltes Ladeprogramm zurückgesetzt wird, wodurch seine Daten nicht mehr verfügbar sind.

Diese Methoden werden in den folgenden Abschnitten ausführlicher beschrieben.

onCreateLoader

Wenn Sie versuchen, z. B. über initLoader() auf ein Ladeprogramm zuzugreifen, wird geprüft, ob das durch die ID angegebene Ladeprogramm vorhanden ist. Andernfalls wird die LoaderManager.LoaderCallbacks-Methode onCreateLoader() ausgelöst. Hier erstellen Sie ein neues Ladeprogramm. In der Regel ist dies eine CursorLoader, Sie können jedoch auch Ihre eigene abgeleitete Loader-Klasse implementieren.

Im folgenden Beispiel erstellt die Callback-Methode onCreateLoader() ein CursorLoader mithilfe ihrer Konstruktormethode, die alle erforderlichen Informationen zum Ausführen einer Abfrage an ContentProvider benötigt. Insbesondere benötigt sie Folgendes:

  • uri: URI für den abzurufenden Inhalt
  • Projektion: Eine Liste der zurückzugebenden Spalten. Bei Übergabe von null werden alle Spalten zurückgegeben. Dies ist ineffizient.
  • selection: Ein Filter, der angibt, welche Zeilen zurückgegeben werden sollen, im Format einer SQL-WHERE-Klausel (ohne WHERE selbst). Bei Übergabe von null werden alle Zeilen für den angegebenen URI zurückgegeben.
  • selectionArgs: Wenn Sie ?s in der Auswahl angeben, werden diese in der Reihenfolge durch die Werte aus selectionArgs ersetzt, in der sie in der Auswahl vorkommen. Die Werte sind als Zeichenfolgen gebunden.
  • sortOrder: Damit werden die als SQL-ORDER BY-Klausel formatierten Zeilen (mit Ausnahme der ORDER BY selbst) sortiert. Bei Übergabe von null wird die Standardsortierreihenfolge verwendet, die möglicherweise nicht geordnet ist.

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

Diese Methode wird aufgerufen, wenn ein zuvor erstellter Loader den Ladevorgang abgeschlossen hat. Diese Methode wird garantiert vor der Veröffentlichung der letzten für diesen Loader bereitgestellten Daten aufgerufen. An diesem Punkt sollten Sie jegliche Nutzung der alten Daten entfernen, da sie freigegeben werden. Geben Sie die Daten jedoch nicht selbst frei. Der Loader besitzt sie und kümmert sich darum.

Das Ladeprogramm gibt die Daten frei, sobald erkannt wird, dass die Anwendung sie nicht mehr verwendet. Wenn die Daten beispielsweise ein Cursor von einem CursorLoader sind, rufen Sie dafür nicht selbst close() auf. Wenn der Cursor in einem CursorAdapter platziert wird, verwenden Sie die Methode swapCursor(), damit die alte Cursor nicht geschlossen wird, wie im folgenden Beispiel gezeigt:

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

Diese Methode wird aufgerufen, wenn ein zuvor erstelltes Ladeprogramm zurückgesetzt wird, wodurch seine Daten nicht mehr verfügbar sind. Mit diesem Callback können Sie herausfinden, wann die Daten demnächst veröffentlicht werden, damit Sie Ihren Verweis darauf entfernen können.

Bei dieser Implementierung wird swapCursor() mit dem Wert null aufgerufen:

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);
}

Beispiel

Im Folgenden sehen Sie als Beispiel die vollständige Implementierung eines Fragment, der ein ListView mit den Ergebnissen einer Abfrage an den Contentanbieter der Kontakte anzeigt. Sie verwendet ein CursorLoader, um die Abfrage beim Anbieter zu verwalten.

Da dieses Beispiel aus einer Anwendung stammt, mit der auf die Kontakte eines Nutzers zugegriffen wird, muss das Manifest die Berechtigung READ_CONTACTS enthalten.

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 onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, 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
    }

    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 onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

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

    @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, 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);
    }

    @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);
    }
}

Weitere Beispiele

Die folgenden Beispiele veranschaulichen die Verwendung von Loadern:

  • LoaderCursor: Eine vollständige Version des vorherigen Snippets.
  • Kontaktliste abrufen: Schritt-für-Schritt-Anleitung, bei der mit einer CursorLoader Daten vom Kontaktanbieter abgerufen werden
  • LoaderThrottle: Ein Beispiel für die Verwendung der Drosselung, um die Anzahl der Abfragen zu reduzieren, die ein Contentanbieter bei Änderungen seiner Daten ausführt.