Avec la version 3.0, nous avons obtenu les fantaisies LoaderManager
, qui gèrent le chargement des données à l’aide des instances AsyncTaskLoader
, CursorLoader
et autres Loader
. Mais en lisant les documents pour ceux-ci, je ne pouvais tout simplement pas comprendre le point: comment sont-ils meilleurs que de simplement utiliser le bon vieux AsyncTask
pour le chargement de données?
Réponse
Trop de publicités?Eh bien, ils sont beaucoup plus simple à mettre en œuvre, et de prendre soin de tout sur la gestion du cycle de vie sont donc beaucoup moins sujettes à erreur.
Il suffit de regarder l'exemple de code, pour montrer le résultat d'un curseur de requête qui permet à l'utilisateur de manière interactive filtrer le résultat par le biais d'une requête champ de saisie dans la barre d'action:
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);
}
}
Correctement mise en œuvre de cet exemple complet de vous-même avec AsyncTask va impliquer beaucoup plus de code... et même alors, allez-vous mettre en œuvre quelque chose d'aussi complet et bien en train de travailler? Par exemple, votre mise en œuvre de conserver le chargé Curseur sur l'activité des modifications de configuration de sorte qu'il n'a pas besoin d'être ré-interrogée lors de la nouvelle instance est créée? LoaderManager/Chargeur permettra de le faire automatiquement pour vous, en tant que bien que prendre soin de la création et de fermeture du Curseur en fonction de l'activité du cycle de vie.
Notez également que l'utilisation de ce code ne nécessite pas que vous pensez à tout au sujet de s'assurer d'exécution long travail est effectué à la sortie de l'UI thread. LoaderManager et CursorLoader prendre soin de tout cela pour vous, assurer que vous ne serez jamais bloquer le thread principal, tout en interagissant avec le curseur. Pour ce faire correctement, vous avez réellement besoin d'avoir Curseur deux objets actifs en même temps dans les points, de sorte que vous pouvez continuer à afficher une INTERFACE utilisateur interactive avec votre Curseur tout en suivant d'un spectacle est en cours de chargement. LoaderManager fait pour vous.
C'est juste beaucoup plus simple API -- pas besoin de savoir à propos de AsyncTask et de réfléchir à ce qui doit s'exécuter en arrière-plan, pas besoin de penser à propos de l'activité du cycle de vie ou comment utiliser les vieux "géré curseur" Api en Activité (qui n'a pas fonctionné aussi bien que LoaderManager de toute façon).
(Btw, n'oubliez pas le nouveau "support" bibliothèque statique qui vous permettent d'utiliser la pleine LoaderManager de l'API sur les anciennes versions d'Android en bas à 1,6!)