从Android3.0开始,Android SDK提供了Loader技术,使用Loader技术可以很容易进行数据的异步加载。Loader技术为我们提供的核心类有:
1.LoaderManager:
可以通过Activity或者的Fragment的getLoaderManager()方法得到LoaderManager,用来对Loader进行管理,一个Activity或者Fragment只能有一个LoaderManager。
2.LoaderManager.LoaderCallbacks:
用于同LoaderManager进行交互,可以在其中创建Loader对象。
3.AsyncTaskLoader:
抽象类,可以进行异步加载数据的Loader,貌似内部也是通过AsynTask实现的,可以通过继承它构建自己的Loader,也可以使用现有的子类,例如异步查询数据库可以使用CursorLoader。
使用Loader一般可以经过以下步骤:
1、初始化Loader,可以使用 initLoader(intid, Bundle args, LoaderManager.LoaderCallbacks callback); 方法进行初始化。
id:
标识Loader的ID,一个Activity或者Fragment只能有一个LoaderManager,但可以有多个Loader,通过ID区 分。在新建Loader时,如果发现已经有相同ID的Loader就会复用该Loader,而不会重新创建。
args:
传给新建Loader的参数。
Callback:
回调接口。
initLoader()方法调用确保了一个loader会被初始化以及激活 它有两种可能的后果:
如果赋予loader的ID已经存在,那么上一个被创建的loader就会被重用
如果赋予loader的ID不存在, initLoader()就会触发 LoaderManager.LoaderCallbacks的onCreateLoader()方法。 这里就是你实例化并返回一个新loader的地方。
2、实现LoaderManager.LoaderCallbacks中的方法,LoaderManager.LoaderCallbacks中需要实现的方法有:
onCreateLoader(int id, Bundle args):
当你试图操作一个loader的时候,(例如通过initLoader()), 会检查被赋予唯一ID的loader是否存在。如果不存在,它会触发LoaderManager.LoaderCallbacks的onCreateLoader()方法。 这是你创建新loader的地方。一般来说被创建的都是CursorLoader,但是你可以实现你自己的Loader子类。
在本例中,onCreateLoader() 回调方法创建了一个CursorLoader。你必须使用它的构造方法建造这个 CursorLoader,构造方法需要向 which ContentProvider执行一次查询的完整信息作为参数。它还需要:尤其地,它还需要:
uri — 要获取的内容的URI。
projection — 返回的列组成的列表,传入null将会返回所有列,但是效率很低。
selection — 一个声明返回哪些行的过滤器,被格式化成类似SQL中WHERE子句的形式(除了没有WHERE自己)。传入null将会返回给定URI的所有行。
selectionArgs — 你可能在Selection中包含一些‘?’,他们将会被selectionArgs的值给替换掉,顺序与它们在selection中出现的顺序一致。 这些值被约束为String类型
sortOrder — 怎样给这些行排顺序,被格式化为类似SQL中ORDER BY子句的形式(除了没有ORDER自己)。传入null 将会使用默认的排序方式,可能是没有顺序。
onLoadFinished(Loader loader, D data):
异步查询结束的会调用这个方法,并返回查询结果 data。
onLoaderReset(Loader loader):
当调用Loader.reset()将Loader数据清空时,但在系统销毁Loader时会自动调用Loader.reset()方法,我们一般不需要手动调用,只需要在onLoaderReset方法中,将使用Loader的移除。
3、使用 restartLoader(intid,Bundleargs,LoaderManager.LoaderCallbacks callback)
进行数据更新,和初始化一个,如果有相同id的Loader存在,会复用Loader,并清空原有Loader中的数据,如果没有就新建一个。这个方法一般使用在需要更新数据时,例如下面例子中,在搜索关键改变时,需要调用这个方法,从新异步查询数据。
下面一个例子使用CursorLoader查询联系人名字,并显示在ListActivity中,在ActionBar上放置了一个SearchView可以根据联系人姓名关键字查询联系人。
Activity代码如下:
public static class CursorLoaderListFragment extends ListFragment
implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
//这就是用来展示列表信息的Adapter。
SimpleCursorAdapter mAdapter;
//如果不是null,这就是当前的搜索过滤器。
String mCurFilter;
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//如果没有数据,就给控件一些文本去显示。
//在真正的应用中,信息来自应用资源。
setEmptyText("No phone numbers");
//我们在action bar中显示一个菜单项。
setHasOptionsMenu(true);
//创建一个新的adapter,我们将用它来显示加载的数据。
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);
//准备loader, 重连到一个已存在的loader,
//或者启动一个新的loader。
getLoaderManager().initLoader(0, null, this);
}
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
//放置一个action bar用于搜索。
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) {
//action bar上的搜索文本改变的时候被调用。
//更新搜索过滤器,并且重启loader用当前的过滤器
//来做新的查询。
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) {
// Insert desired behavior here.
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) {
//当需要创建一个新的loader时被调用。
//本例中仅有一个loader,所以我们不必关心ID的问题。
//首先,根据我们当前是否正在过滤,
//选择base URI来使用。
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
//现在创建并返回一个CursorLoader,
//它将会为被显示的数据创建一个Cursor。
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) {
//把新的cursor换进来。 (框架将会在我们返回的时候
//管理旧cursor的关闭事宜。)
mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) {
//当最后一个Cursor进入onLoadFinished()的时候被调用。
//cursor将要被关闭, 我们应该确保
//不再使用它。
mAdapter.swapCursor(null);
}
}