Android 中利用 ContentResolver 查询数据库的四种方式

直接使用 ContentResolver 的 query 方法在主线程中查询数据(非异步查询)

由于是在主线程中执行查询操作,为了不阻塞主线程,尽量在数据量小的时候使用,而且要注意及时关闭 cursor

代码示例

1
2
3
Cursor cursor = getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
// 获得 cursor 中数据 ...
cursor.close();

利用 Activity 的 managedQuery 方法在主线程中查询数据(非异步查询)

相对于上一种方式,我们不需要处理 cusor 的关闭,但是同样只适合在数据量小的时候。

对于 Android 3.0 HONEYCOMB 以上的版本,Android 官方建议使用 LoaderManager 替代,这也是下一种查询方式。

代码示例:

1
2
Cursor cursor = managedQuery(ContactsContract.Data.CONTENT_URI, null, null, null, null);
// 获得 cursor 中数据 ...

利用 LoaderManager 机制中的 CursorLoader 异步查询数据库(异步查询)

Android3.0 以上可以直接使用,3.0 以下需要使用 support 库.

注:LoaderManager 开始一个 Loader 之后,如果没有调用 destroyLoader 方法,在 activity 的 onStart 时候会重新开始 Loader 中的任务

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class SampleListActivity extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
private static final String[] PROJECTION = new String[] { "_id", "text_column" };
// The loader's unique id. Loader ids are specific to the Activity or
// Fragment in which they reside.
private static final int LOADER_ID = 1;
// The callbacks through which we will interact with the LoaderManager.
private LoaderManager.LoaderCallbacks<Cursor> mCallbacks;
// The adapter that binds our data to the ListView
private SimpleCursorAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] dataColumns = { "text_column" };
int[] viewIDs = { R.id.text_view };
// Initialize the adapter. Note that we pass a 'null' Cursor as the
// third argument. We will pass the adapter a Cursor only when the
// data has finished loading for the first time (i.e. when the
// LoaderManager delivers the data to onLoadFinished). Also note
// that we have passed the '0' flag as the last argument. This
// prevents the adapter from registering a ContentObserver for the
// Cursor (the CursorLoader will do this for us!).
mAdapter = new SimpleCursorAdapter(this, R.layout.list_item,
null, dataColumns, viewIDs, 0);
// Associate the (now empty) adapter with the ListView.
setListAdapter(mAdapter);
// The Activity (which implements the LoaderCallbacks<Cursor>
// interface) is the callbacks object through which we will interact
// with the LoaderManager. The LoaderManager uses this object to
// instantiate the Loader and to notify the client when data is made
// available/unavailable.
mCallbacks = this;
// Initialize the Loader with id '1' and callbacks 'mCallbacks'.
// If the loader doesn't already exist, one is created. Otherwise,
// the already created Loader is reused. In either case, the
// LoaderManager will manage the Loader across the Activity/Fragment
// lifecycle, will receive any new loads once they have completed,
// and will report this new data back to the 'mCallbacks' object.
LoaderManager lm = getLoaderManager();
lm.initLoader(LOADER_ID, null, mCallbacks);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// Create a new CursorLoader with the following query parameters.
return new CursorLoader(SampleListActivity.this, CONTENT_URI,
PROJECTION, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// A switch-case is useful when dealing with multiple Loaders/IDs
switch (loader.getId()) {
case LOADER_ID:
// The asynchronous load is complete and the data
// is now available for use. Only now can we associate
// the queried Cursor with the SimpleCursorAdapter.
mAdapter.swapCursor(cursor);
break;
}
// The listview now displays the queried data.
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// For whatever reason, the Loader's data is now unavailable.
// Remove any references to the old data by replacing it with
// a null Cursor.
mAdapter.swapCursor(null);
}
}

使用 Android 提供的异步查询框架 AsyncQueryHandler(异步查询)

AsyncQueryHandler 对比 CursorLoader 来说, 适合单次查询获得结果的情况。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static final int TOKEN_QUERY_CONTACT = 1;
private MyAsyncQueryHandler mQueryHandler;
mQueryHandler = new MyAsyncQueryHandler(getContentResolver());
mQueryHandler.startQuery(TOKEN_QUERY_CONTACT, null, ContactsContract.Data.CONTENT_URI, null, null, null, null);
private final class MyAsyncQueryHandler extends AsyncQueryHandler {
public MyAsyncQueryHandler (ContentResolver cr) {
super(cr);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
if(null == cursor) {
return;
}
if(token == TOKEN_QUERY_CONTACT) {
// 获得 cursor 中数据 ...
}
}
}

总结

  1. 耗时非常短的操作可以直接用第一种方式

  2. 对于只需要查询一次,不管数据量大小我觉得都最后使用 AsyncQueryHandler

  3. 对于需要多次查询操作的,推荐使用 LoaderManager 机制的 CursorLoader

  4. 数据库的异步更新、插入和删除推荐使用 AsyncQueryHandler

参考文章:

END
Johnny Shieh wechat
我的公众号,不只有技术,还有咖啡和彩蛋!