1. 装载器API概要
2. 在应用中使用装载器
2.1 启动装载器
2.2 重启装载器
3. 实例
3.1 更多实例
在android 3.0中引入了装载器,它使得在活动或碎片内异步加载数据变得容易。装载程序有如下特性:
- 它们对每个活动和碎片都是可用的。
- 它们提供异步的数据加载。
- 它们监控着活动和碎片的数据源,并在数据改变时递送新结果。
- 配置改变后,当活动和碎片被重建时,它们自动重新连接到最后装载器的游标。因此,活动和碎片不必再次查询它们的数据。
1. 装载器API概要
在应用里使用装载器的过程中可能有多个类和接口被调用。它们被总结在下面的这个表中:
| 类/接口 | 描述 |
|
| 与 每个活动或碎片仅有一个 |
| 与 | |
| 执行异步数据加载的抽象类。它是装载器的基类。通常,你将使用 | |
| 提供一个 | |
|
|
上表中的类和接口是你在应用中用来实现装载器的基本组件。你不需要所有这些为你创建的每个装载机,但是你通常需要一个LoaderManager引用以便初始化装载器以及Loader类的实现,譬如CursorLoader。下面的章节展示在应用中如何使用这些类和接口。
2. 在应用中实例装载器
本章节描述在Android应用中如何使用装载器。使用装载器的应用基本上包括以下部分:
Activity或Fragment.LoaderManager实例.CursorLoader,以加载由ContentProvider提供的数据。 作为选择,你可以实现你自己的Loader或AsyncTaskLoader的子类来加载来自其他数据源的数据。
LoaderManager.LoaderCallbacks的实现。这就是你创建新的装载器以及管理现有装载器引用的地方。- 显示装载器数据的方式,比如
SimpleCursorAdapter。 - 数据源,比如
ContentProvider, 当使用CursorLoader时。
2.1 启动装载器
在管理一个或多个Activity或Fragment里,LoaderManagerLoader实例。 每个活动或碎片仅有一个。LoaderManager
通常,在活动的onCreate()方法内初始化Loader或者是在碎片的onActivityCreated()方法内。像下面这样做:
// 准备装载器。重连一个现有的装载器,或者启动一个新的。
getLoaderManager().initLoader(0, null, this);
initLoader()方法携带以下参数:
- 唯一的ID,它标识了装载器。在这个例子中,ID为0。
- 可选的参数,在构造器中提供给装载器(本例中为null)。
一个LoaderManager.LoaderCallbacks实现,LoaderManager调用它来报告装载器事件。本例中,本地类实现了LoaderManager.LoaderCallbacks接口,所以它专递了一个自身的引用,即this。
initLoader()调用确保了装载器被初始化且是激活的。 此调用可能有两种结果:call ensures that a loader is initialized and active. It has two possible out comes:
- 如果由ID指定的装载器已经存在,则最后创建的装载器被再次使用。
- 如果由ID指定的装载器不存在,
initLoader()将触发LoaderManager.LoaderCallbacks方法onCreateLoader()。这就是你实现代码来初始化并返回一个新的装载器的地方。更多讨论,查阅onCreateLoader章节。
在任何一种情况下,给定的LoaderManager.LoaderCallbacks实现与装载器是相关联的,并且当装载器状态改变时被调用。如果在此调用点装载器处于它的启动状态,且请求的装载器已经存在,那么系统则立即调用onLoadFinished()(在initLoader()期间),所以你必须为这种情的出现做好准备。更多次回调的讨论,请查阅onLoadFinished。
注意, initLoader()方法返回被创建的Loader,但是你不需要获得它的引用。LoaderManager自动管理着装载器的生命。LoaderManager启动并在必要时停止加载,而且维持着装载器的状态和它有关联的内容。这意味着,很少与装载器直接交互(不过对于使用装载器方法来微调装载器行为的例子,请查阅LoaderThrottle实例)。当特定事件发生时,通常你必须使用LoaderManager.LoaderCallbacks方法来介入加载过程。对于更多该主题的讨论,请查阅使用LoaderManager回调章节。
2.2 重启装载器
如上所示,当使用initLoader()时,它使用一个具有指定ID的现有装载器,如果存在一个的话。如果没有,则创建一个。不过有时你打算丢弃旧的数据然后重新开始。
为了丢弃旧数据,你需要使用restartLoader()。例如,下面这个SearchView.OnQueryTextListener实现在用户的查询数据发生变化时重启了装载器:
public boolean onQueryTextChanged(String newText) {
// 在当动作栏搜索文本发生变化时被调用。
// 更新搜索过滤,然后重启装载器用这个过滤器来执行一条新的查询。
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager().restartLoader(0, null, this);
return true;
}
2.3 使用LoaderManager回调
LoaderManager.LoaderCallbacks是一个允许客户端与LoaderManager交互的回调接口。
在特殊的CursorLoader内,装载器被希望用来在客户端被停止后获取他们的数据。这允许应用跨越活动或碎片的onStop()和onStart()方法来保持它们的数据,所以当用户返还到应用时,他们不必等待数据重新加载。当知道何时创建一个新的装载器时,使用LoaderManager.LoaderCallbacks接口方法,然后告诉应用何时停止使用装载器的数据。
LoaderManager.LoaderCallbacks接口包括这些方法:
onCreateLoader()— 实例化一个新的指定ID的。Loader,然后返回它
onLoadFinished()— 当先前创建的装载器完成它的加载时调用。
onLoaderReset()— 当先前的装载器正在重置时调用,因此使得它的数据不可用。
这些方法在接下来的章节里有更为详细地描述。
onCreateLoader
当试图访问装载器时(例如,通过initLoader()),它来检查通过制定ID的装载器是否存在。如果不存在,它触发LoaderManager.LoaderCallbacks的onCreateLoader()方法。这就是创建新的装载器的地方。通常,这可能是CursorLoader,但是你可以实现你自己的Loader子类。
在此例中,Loader回调方法创建一个CursorLoader。创建它必须使用CursorLoader的构造函数,它要求一套完整的需要来执行ContentProvider查询的信息。它尤其需要:
- uri — 检索内容的URI。
- projection — 返回哪些列的清单。传递null将返回所以列,但效率不高。
- selection — 一个声明返回哪些行的过滤器,其格式如同SQL WHERE子句(不包括WHERE本身)。传递null将返回给定URI的全部行。
- selectionArgs — 在selection中可以包含 ?符号,这将被
的值所替代,它们按顺序出现在来自selectionArgs中。这些值限制为String。selection - sortOrder — 如何读行排序,格式如同SQL ORDER BY字句(不包括ORDER BY本身)。传递null将使用默认的存储顺序,它可能是无序的。
例如:
// 如果非空,它是用户提供的当前过滤器。
String mCurFilter;
...
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// 在需要创建一个新的装载器时调用它。
// 该例子中只有一个装载器,所以不用在乎ID。
// 首先,依当前我们是否正在过滤来选择基准URI来用。
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// 现在创建并返回一个CursorLoader,它将负责创建正被显示的数据游标。
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");
}
onLoaderReset
当先前创建的装载器正在被重置时调用这个方法,因此致使它的数据不可用。通过此函数可以知道数据何时将要被释放,这样你就可以移除你对它的引用。
下面的实现以一个null值调用:swapCursor()
// 这是用来显示列表数据的适配器。
SimpleCursorAdapter mAdapter;
...
public void onLoaderReset(Loader<Cursor> loader) {
//当最后提供给onLoadFinished()的Cursor将要被关闭时被调用。我们需要确保不再使用它。
mAdapter.swapCursor(null);
}
3. 实例
作为实例,下面是一个的完整实现,它显示了一个Fragment,其包含有对联系人内容提供者进行查询的结果ListView。实现使用来管理在提供者上的查询。CursorLoader
对于一个访问用户联系人的应用,如此例所示,它的清单文件中必须包括权限。READ_CONTACTS
public static class CursorLoaderListFragment extends ListFragment
implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
// 用来显示列表数据的适配器。
SimpleCursorAdapter mAdapter;
// 如果非空,这是用户提供的当前过滤器。
String mCurFilter;
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// 如果没有数据,提供一些文本来显示。
// 在真正的应用中这可能出自于资源。
setEmptyText("No phone numbers");
//在动作栏显示一个菜单项
setHasOptionsMenu(true);
//创建一个空的适配器,我们将使用它来显示被加载的数据。
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);
//准备装载器。要么与一个现有的装载器重新连接,要么启动一个新的装载器。
getLoaderManager().initLoader(0, null, this);
}
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
//放置一个动作栏项用来搜索。
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) {
//当动作栏搜索文本改变时调用。
// 更新搜索过滤器,并重启装载器用这个过滤器来执行新的查询。
mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
getLoaderManager().restartLoader(0, null, this);
return true;
}
@Override public boolean onQueryTextSubmit(String query) {
//不要在乎它。
return true;
}
@Override public void onListItemClick(ListView l, View v, int position, long id) {
// 这里插入期望的行为。
Log.i("FragmentComplexList", "Item clicked: " + id);
}
//这些是将要取回的联系人数据行。
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) {
// 当需要创建一个新的装载器时调用它。本例中仅有一个装载器,所以不必在乎ID。
// 首先,依当前是否正在过滤,选择基准URI来用。
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
// 现在创建并返回一个CursorLoader,它将负责创建正被显示的数据的游标.
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) {
// 交换新的游标。 (一旦返回,框架将负责关闭老的游标)。
mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) {
// 当提供给onLoadFinished()的最后的游标将要关闭时调用。
// 需要确保我们不在使用它。
mAdapter.swapCursor(null);
}
}
3.1 更多实例
在ApiDemos中有很多不同的例子,它们说明了如何使用装载器:
- LoaderCursor — 如上所示代码段的完整版。
- LoaderThrottle — 一个如何使用节流来减少内容提供者执行查询的数目,当它的数据发生变化时。
更多下载好安装SDK实例的信息,请参阅 Getting the Samples 。
904

被折叠的 条评论
为什么被折叠?



