使用Loaders异步加载数据
2012-06-29 22:27
507 查看
Loaders
IN THIS DOCUMENT
Loader API SummaryUsing Loaders in an Application
Starting a Loader
Restarting a Loader
Using the LoaderManager Callbacks
Example
More Examples
KEY CLASSES
LoaderManager
Loader
RELATED SAMPLES
LoaderCursorLoaderThrottle
Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:
They are available to every
Activityand
Fragment.
They provide asynchronous loading of data.
They monitor the source of their data and deliver new results when the content changes.
They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data.
Loader API Summary
There are multiple classes and interfaces that may be involved in using loaders in an application. They are summarized in this table:Class/Interface | Description |
---|---|
LoaderManager | An abstract class associated with an Activityor Fragmentfor managing one or more Loaderinstances. This helps an application manage longer-running operations in conjunction with the Activityor Fragmentlifecycle; the most common use of this is with a CursorLoader, however applications are free to write their own loaders for loading other types of data. There is only one LoaderManagerper activity or fragment. But a LoaderManagercan have multiple loaders. |
LoaderManager.LoaderCallbacks | A callback interface for a client to interact with theLoaderManager. For example, you use the onCreateLoader()callback method to create a new loader. |
Loader | An abstract class that performs asynchronous loading of data. This is the base class for a loader. You would typically useCursorLoader, but you can implement your own subclass. While loaders are active they should monitor the source of their data and deliver new results when the contents change. |
AsyncTaskLoader | Abstract loader that provides an AsyncTaskto do the work. |
CursorLoader | A subclass of AsyncTaskLoaderthat queries the ContentResolverand returns a Cursor. This class implements the Loaderprotocol in a standard way for querying cursors, building on AsyncTaskLoaderto perform the cursor query on a background thread so that it does not block the application's UI. Using this loader is the best way to asynchronously load data from a ContentProvider, instead of performing a managed query through the fragment or activity's APIs. |
to the
LoaderManagerin
order to initialize a loader and an implementation of a
Loaderclass
such as
CursorLoader.
The following sections show you how to use these classes and interfaces in an application.
Using Loaders in an Application
This section describes how to use loaders in an Android application. An application that uses loaders typically includes the following:An
Activityor
Fragment.
An instance of the
LoaderManager.
A
CursorLoaderto
load data backed by a
ContentProvider.
Alternatively, you can implement your own subclass of
Loaderor
AsyncTaskLoaderto
load data from some other source.
An implementation for
LoaderManager.LoaderCallbacks.
This is where you create new loaders and manage your references to existing loaders.
A way of displaying the loader's data, such as a
SimpleCursorAdapter.
A data source, such as a
ContentProvider,
when using a
CursorLoader.
Starting a Loader
The LoaderManagermanages
one or more
Loaderinstances
within an
Activityor
Fragment.
There is only one
LoaderManagerper
activity or fragment.
You typically initialize a
Loaderwithin
the activity's
onCreate()method,
or within the fragment's
onActivityCreated()method.
You do this as follows:
// Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this);
The
initLoader()method
takes the following parameters:
A unique ID that identifies the loader. In this example, the ID is 0.
Optional arguments to supply to the loader at construction (
nullin this example).
A
LoaderManager.LoaderCallbacksimplementation,
which the
LoaderManagercalls
to report loader events. In this example, the local class implements the
LoaderManager.LoaderCallbacksinterface,
so it passes a reference to itself,
this.
The
initLoader()call
ensures that a loader is initialized and active. It has two possible outcomes:
If the loader specified by the ID already exists, the last created loader is reused.
If the loader specified by the ID does not exist,
initLoader()triggers
the
LoaderManager.LoaderCallbacksmethod
onCreateLoader().
This is where you implement the code to instantiate and return a new loader. For more discussion, see the section onCreateLoader.
In either case, the given
LoaderManager.LoaderCallbacksimplementation
is associated with the loader, and will be called when the loader state changes. If at the point of this call the caller is in its started state, and the requested loader already exists and has generated its data, then the system calls
onLoadFinished()immediately
(during
initLoader()),
so you must be prepared for this to happen. See onLoadFinished for more discussion of this callback
Note that the
initLoader()method
returns the
Loaderthat
is created, but you don't need to capture a reference to it. The
LoaderManagermanages
the life of the loader automatically. The
LoaderManagerstarts
and stops loading when necessary, and maintains the state of the loader and its associated content. As this implies, you rarely interact with loaders directly (though for an example of using loader methods to fine-tune a loader's behavior, see the LoaderThrottle sample).
You most commonly use the
LoaderManager.LoaderCallbacksmethods
to intervene in the loading process when particular events occur. For more discussion of this topic, see Using the LoaderManager
Callbacks.
Restarting a Loader
When you use initLoader(),
as shown above, it uses an existing loader with the specified ID if there is one. If there isn't, it creates one. But sometimes you want to discard your old data and start over.
To discard your old data, you use
restartLoader().
For example, this implementation of
SearchView.OnQueryTextListenerrestarts
the loader when the user's query changes. The loader needs to be restarted so that it can use the revised search filter to do a new query:
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; }
Using the LoaderManager Callbacks
LoaderManager.LoaderCallbacksis
a callback interface that lets a client interact with the
LoaderManager.
Loaders, in particular
CursorLoader,
are expected to retain their data after being stopped. This allows applications to keep their data across the activity or fragment's
onStop()and
onStart()methods,
so that when users return to an application, they don't have to wait for the data to reload. You use the
LoaderManager.LoaderCallbacksmethods
when to know when to create a new loader, and to tell the application when it is time to stop using a loader's data.
LoaderManager.LoaderCallbacksincludes
these methods:
onCreateLoader()—
Instantiate and return a new
Loaderfor
the given ID.
onLoadFinished()—
Called when a previously created loader has finished its load.
onLoaderReset()—
Called when a previously created loader is being reset, thus making its data unavailable.
These methods are described in more detail in the following sections.
onCreateLoader
When you attempt to access a loader (for example, through initLoader()),
it checks to see whether the loader specified by the ID exists. If it doesn't, it triggers the
LoaderManager.LoaderCallbacksmethod
onCreateLoader().
This is where you create a new loader. Typically this will be a
CursorLoader,
but you can implement your own
Loadersubclass.
In this example, the
onCreateLoader()callback
method creates a
CursorLoader.
You must build the
CursorLoaderusing
its constructor method, which requires the complete set of information needed to perform a query to the
ContentProvider.
Specifically, it needs:
uri — The URI for the content to retrieve.
projection — A list of which columns to return. Passing
nullwill return all columns, which is inefficient.
selection — A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing
nullwill
return all rows for the given URI.
selectionArgs — You may include ?s in the selection, which will be replaced by the values from selectionArgs, in the order that they appear in the selection. The values will be bound as Strings.
sortOrder — How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing
nullwill
use the default sort order, which may be unordered.
For example:
// 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"); }
onLoadFinished
This method is called when a previously created loader has finished its load. This method is guaranteed to be called prior to the release of the last data that was supplied for this loader. At this point you shouldremove all use of the old data (since it will be released soon), but should not do your own release of the data since its loader owns it and will take care of that.
The loader will release the data once it knows the application is no longer using it. For example, if the data is a cursor from a
CursorLoader,
you should not call
close()on
it yourself. If the cursor is being placed in a
CursorAdapter,
you should use the
swapCursor()method
so that the old
Cursoris
not closed. For example:
// 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); }
onLoaderReset
This method is called when a previously created loader is being reset, thus making its data unavailable. This callback lets you find out when the data is about to be released so you can remove your reference toit.
This implementation calls
swapCursor()with
a value of
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); }
Example
As an example, here is the full implementation of a Fragmentthat
displays a
ListViewcontaining
the results of a query against the contacts content provider. It uses a
CursorLoaderto
manage the query on the provider.
For an application to access a user's contacts, as shown in this example, its manifest must include the permission
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);
}
}
More Examples
There are a few different samples in ApiDemos that illustrate how to use loaders:LoaderCursor — A complete version of the snippet shown
above.
LoaderThrottle — An example of how to use throttling
to reduce the number of queries a content provider does when its data changes.
For information on downloading and installing the SDK samples, see Getting the Samples.
相关文章推荐
- WinForm中异步加载数据并使用进度条
- Android_Loader_使用LoaderManager管理Loader实现异步动态加载数据
- echarts异步数据加载使用总结
- 使用CursorLoader异步加载数据
- jsTree基本使用(新增,修改,删除,移动,点击,加载默认选中根节点,异步加载数据)
- 使用NSoperation多线程异步加载图片数据
- 24.使用getJSON()方法异步加载JSON格式数据
- 使用CursorLoader异步加载数据
- Echarts使用心得总结——异步数据加载与更新(二)
- [Android Pro] 使用CursorLoader异步加载数据 from 3.0
- jquery使用EasyUI Tree异步加载JSON数据(生成树)
- 使用爬虫抓取网站异步加载数据
- android开发异步加载网络数据AsyncTask的使用
- C#异步加载数据:BackgroundWorker的使用
- Android 使用Android-Universal-Image-Loader进行异步数据加载
- asp.net 使用js分页实现异步加载数据
- MVC3----使用Jquery模板异步加载数据
- Android之数据存储----使用LoaderManager异步加载数据库
- booklet翻书插件使用——异步加载数据
- Loaders异步加载数据的方式