CursorLoader是从API 11开始引入的。一般涉及数据库的时候,都会用到cursor,如果是你设计的应用本地应用还好,在加载数据时cursor使用场景不会很频繁复杂。如若是联网的应用,面对的场景就会比较复杂,你的应用随时会从网上获取到新的内容并存储到数据库中,与此同时,你随时需要对界面的内容进行更新,所以你要管理好你的cursor: (a)对数据库进行监听,在数据变化时更新你的cursor,(b)加载新cursor时,要关闭旧的cursor。而CursorLoader解决上述的问题,使你能更加专注于android的应用的实现,无需关注一些细枝末节。CursorLoader可以当作一个cursor装载器,负责上述与cursor相关的事情。
CursorLoader的继承关系
java.lang.Object↳ android.content.Loader<D>
↳ android.content.AsyncTaskLoader<D>
↳ android.content.CursorLoader
其中,Loader,AsyncTaskLoader也都是从API 11引入的。从上面图示看到它继承自Loader,所以它是Loader的一种。它同样使用contentresolver方式来请求数据库,返回一个cursor。
如何使用CursorLoader?
创建 cursorloader 必须要指定请求信息,来保证 CursorLoader 内部会返回一个你需要的 cursor创建的两种方式:
A. 直接创建。可以直接在创建时候指定请求信息。
public CursorLoader ( Context context, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
Added in API level 11
Creates a fully-specified CursorLoader. See ContentResolver.query() for documentation on the meaning of the parameters. These will be passed as-is to that call.
CursorLoader 会根据系统提供的请求字段在 loadInbackground() 中去查询数据库并得到一个 cursor,
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
mSelectionArgs, mSortOrder, mCancellationSignal);
B. 填充信息。
public CursorLoader ( Context context)
Added in API level 11
Creates an empty unspecified CursorLoader. You must follow this with calls to setUri(Uri) , setSelection(String) , etc to specify the query to perform.
先创建一个空的实例对象,再调用以下两个函数
setUri(Uri) , setSelection(String) , setSelectionArgs(String[]) , setSortOrder(String)
setProjection(String[]) .
本质上上面两个函数都是一样的,在源码,可以看到下面几个变量,
Uri mUri;
String[] mProjection;
String mSelection;
String[] mSelectionArgs;
String mSortOrder;
这些正是保存请求信息的几个变量的引用,两种不同的创建方式,也就是两个不同的赋值函数,它们最终都是调用 上面的 cursor 的查询函数。
了解它的调用 的过程:
几个重要的回调方法:
public Cursor loadInBackground ()该方法是在一个独立于UI线线程的工作线程中执行的。在这个方法中,完成了cursor的获取过程,然后将这个结果返回。该方法不会直接对外返回该结果,返回的cursor会在deliverResult(D)中被调用,这个方法是UI线程中执行的。如果你需要在UI线程里对cursor做一些操作,就可以在deliever(D)方法里执行。你可以根据自己的需要来定制自己的cusor。
下面是loadInBackground()的源码部分:
public Cursor loadInBackground() {
synchronized (this) {
if (isLoadInBackgroundCanceled()) {
throw new OperationCanceledException();
}
mCancellationSignal = new CancellationSignal();
}
try {
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
mSelectionArgs, mSortOrder, mCancellationSignal);
if (cursor != null) {
try {
// Ensure the cursor window is filled.
cursor.getCount();
cursor.registerContentObserver(mObserver);
} catch (RuntimeException ex) {
cursor.close();
throw ex;
}
}
return cursor;
} finally {
synchronized (this) {
mCancellationSignal = null;
}
}
}
其中查询的cursor已经注册了监听器
ForceLoadContentObserver
所以当cursor有更新变化时,它会回调其中的onChange()函数,其中onChange()会回调onContentChanged()函数。所以,你可以在此函数中重写你需要执行的操作。所以这也是CursorLoader易用的原因。
public void deliverResult (D data)
该方法将loadInbackground(D data)返回的结果发送到注册的监听器。该方法只有被继承的子类调用,也必须在UI线程中调用。
在代码中如何应用CursorLoader?
下面是使用的CursorLoader的一个例子。在android的更新里面,与CursorLoader同时引进的还有LoaderManager(在此先不做详细说明),这个类用来管理各种Loader。
public class ContactFragment extends ListFragment implements OnQueryTextListener,
OnCloseListener, LoaderCallbacks<Cursor> {
private SearchView mSearchView;
private String mFilter;
private SimpleCursorAdapter mAdapter;
private static final String[] QUERY_PROJECTION = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY
};
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
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});
setListAdapter(mAdapter);
setListShown(false);
//init the loader.
getLoaderManager().initLoader(0, null, this);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Auto-generated method stub
mSearchView = new MySearchView(getActivity());
mSearchView.setOnCloseListener(this);
mSearchView.setOnQueryTextListener(this);
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
mSearchView.setIconifiedByDefault(true);
item.setActionView(mSearchView);
MenuItem insert = menu.add("Insert");
insert.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
testInsert();
return false;
}
});
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri baseUri = null;
if (mFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+ Contacts.DISPLAY_NAME + " != '' ))";
//Create a CursorLoader to get the data.
return new CursorLoader(getActivity(), baseUri,
QUERY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
//Load the data
mAdapter.swapCursor(arg1);
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
// TODO Auto-generated method stub
mAdapter.swapCursor(null);
}
@Override
public boolean onClose() {
if (!TextUtils.isEmpty(mSearchView.getQuery())) {
mSearchView.setQuery("", true);
}
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
String newFilter = !TextUtils.isEmpty(newText) ? newText : null;
if (null == newFilter && null == mFilter) {
return true;
}
if (null != mFilter && mFilter.equals(newFilter)) {
return true;
}
mFilter = newFilter;
getLoaderManager().restartLoader(0, null, this);
return false;
}
@Override
public boolean onQueryTextSubmit(String arg0) {
// TODO Auto-generated method stub
return false;
}
/**
* A customized searchView used to search the information you want with key words filter.
* ClassName: MySearchView <br/>
* Function: TODO ADD FUNCTION. <br/>
* Reason: TODO ADD REASON(可选). <br/>
* date: Apr 23, 2014 12:02:57 AM <br/>
*
* @author Lawrence
* @version ContactFragment
* @since JDK 1.6
*/
class MySearchView extends SearchView {
public MySearchView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
@Override
public void onActionViewCollapsed() {
// TODO Auto-generated method stub
super.onActionViewCollapsed();
setQuery("", false);
}
}
private int i = 0;
//insert a contact to database.
public void testInsert(){
ContentValues values = new ContentValues();
//first get a rawContactId by inserting a null value to RawContact table
Uri rawContactUri = getActivity().getContentResolver().insert(RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
//insert a name to data table
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
values.put(StructuredName.DISPLAY_NAME, "Lawrence" + (i++));
//insert the real values
getActivity().getContentResolver().insert(Data.CONTENT_URI,values);
//insert a phone number
values.clear();
values.put(Phone.RAW_CONTACT_ID, rawContactId);
//String "Data.MIMETYPE":The MIME type of the item represented by this row
//String "CONTENT_ITEM_TYPE": MIME type used when storing this in data table.
values.put(Data.MIMETYPE,Phone.CONTENT_ITEM_TYPE);
values.put(Phone.NUMBER,"13410539352");
getActivity().getContentResolver().insert(Data.CONTENT_URI,values);
}
}
这是一个利用CursorLoader来查询联系人,然后加载联系人的例子,效果图如下图所示。Actionbar中的Insert按钮可以向数据库插入一条联系人数据,点击这个按钮后,程序会自动将数据更新显示到 UI界面,这个正是CursorLoader的自我更新工作,
例子源码下载地址:
http://download.csdn.net/detail/a2758963/7242579
参考文章:
http://developer.android.com/reference/android/app/LoaderManager.html