1. 概念:
在HoneyComb3.0中提出了新的异步数据加载特性(ContentLoader),它可以异步的从后台获取数据并更新UI界面。CursorLoader类是专门为此
特性设计的,他允许程序通过ContentProvider异步的从数据中读取数据,并将获取的数据显示到UI界面中。
2. 效果图:
(1)主操作界面,在Action|Bar中存在两个"快捷按钮",如下图:
(2)单击"加载按钮"后从数据库中动态的加载数据(当前数据库只添加了一条数据),如下图:
(3)间隔5秒后继续从数据库中读取数据(数据库被动态的添加了数据,并调用ContentResovler的notifiyChange()方法,该方法不调5秒后不
会加载数据):
3. 功能实现:
(1)主Activity代码:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 获取FragmentManager实例。
*/
FragmentManager mFragmentManager = getFragmentManager();
if (mFragmentManager.findFragmentById(android.R.id.content) == null) {
/**
* 创建ContentLoaderFragment,这是一个ListFragment,用于显示結果。
*/
ContentLoaderFragment mContentLoaderFragment = new ContentLoaderFragment();
/**
* 通过FragmentManager向Activity中添加Fragment。
*/
mFragmentManager.beginTransaction().add(android.R.id.content, mContentLoaderFragment).commit();
}
}
}
(2)查询結果显示器,ListFragment代码:
/**
* 给ListFragment中的ListView组件设置Adapter实现。
*/
setListAdapter(mSimpleCursorAdapter);
/**
* 通过LoaderManager初始化ContentLoader,initLoader()方法的等三个参数是LoaderCallbacks实现,
* 在Callbacks实现中创建ContentLoader实例,并设置多长时间通过ContentLoader去异步的加载数据,并刷新界面显示。
*/
getLoaderManager().initLoader(0, null, new ContentLoaderCallBack(getActivity(), mSimpleCursorAdapter));
}
/**
* 向ActionBar中添加"快捷按钮"。
*/
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(Menu.NONE, POPULATE_ID, 0, "Populate Letter").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(Menu.NONE, CLEAR_ID, 0, "Clear").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
/**
* 单击ActionBar中添加"快捷按钮"触发相应事件。
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
final ContentResolver cr = getActivity().getContentResolver();
switch (item.getItemId()) {
case POPULATE_ID :
if (mPopulatingTask != null) {
mPopulatingTask.cancel(false);
}
/**
* 异步的通过ContentProvider向数据库中添加数据。
*/
mPopulatingTask = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
for (char c = 'A'; c <= 'K'; c++) {
if (isCancelled()) {
break;
}
StringBuilder builder = new StringBuilder("Data ");
builder.append(c);
ContentValues values = new ContentValues();
values.put(ContentLoaderConstant.COLUMN_NAME_DATA, builder.toString());
cr.insert(ContentLoaderConstant.CONTENT_URI, values);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
return null;
}
};
mPopulatingTask.execute((Void [])null);
return true;
case CLEAR_ID:
if (mPopulatingTask != null) {
mPopulatingTask.cancel(false);
mPopulatingTask = null;
}
/**
* 异步的通过ContentProvider清除数据库中的数据。
*/
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
cr.delete(ContentLoaderConstant.CONTENT_URI, null, null);
return null;
}
};
task.execute((Void[])null);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
(3)ContentLoader回调方法:
private SimpleCursorAdapter mSimpleCursorAdapter;
public ContentLoaderCallBack (Context mContext, SimpleCursorAdapter mSimpleCursorAdapter) {
this.mContext = mContext;
this.mSimpleCursorAdapter = mSimpleCursorAdapter;
}
/**
* 创建CursorLoader(异步数据加载器),并设置更新时间,这个Loader会根据更新时间从ContentProvider中加载数据。
*/
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader mCursorLoader = new CursorLoader(mContext, ContentLoaderConstant.CONTENT_URI, ContentLoaderConstant.PROJECTION, null, null, null);
mCursorLoader.setUpdateThrottle(5000);
return mCursorLoader;
}
/**
* 加载完成刷新界面显示。
*/
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor mCursor) {
mSimpleCursorAdapter.swapCursor(mCursor);
}
/**
* 加载完成重置界面显示。
*/
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mSimpleCursorAdapter.swapCursor(null);
}
}
(4)ContentProvider代码:
private final HashMap<String, String> mNotesProjectionMap;
private DataBaseHelper mDataBaseOpenHelper;
public ContentLoaderProvider() {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mUriMatcher.addURI(ContentLoaderConstant.AUTHORITY, ContentLoaderConstant.TABLE_NAME, ContentLoaderConstant.FOCUS);
mUriMatcher.addURI(ContentLoaderConstant.AUTHORITY, ContentLoaderConstant.TABLE_NAME + "/#", ContentLoaderConstant.FOCUS_ID);
mNotesProjectionMap = new HashMap<String, String>();
mNotesProjectionMap.put(ContentLoaderConstant._ID, ContentLoaderConstant._ID);
mNotesProjectionMap.put(ContentLoaderConstant.COLUMN_NAME_DATA, ContentLoaderConstant.COLUMN_NAME_DATA);
}
@Override
public boolean onCreate() {
mDataBaseOpenHelper = new DataBaseHelper(getContext());
return true;
}
/**
* 向数据库中插入数据,并notifyChange告诉CursorLoader加载数据,如果不调用notifyChange那么CursorLoader不会来加载数据
* 即使给CursorLoader设置的取数据的间隔时间,它也不会来取数据,除非调用了notifyChange他才会来取。
*/
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
if (mUriMatcher.match(uri) != ContentLoaderConstant.FOCUS) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
if (values.containsKey(ContentLoaderConstant.COLUMN_NAME_DATA) == false) {
values.put(ContentLoaderConstant.COLUMN_NAME_DATA, "");
}
SQLiteDatabase db = mDataBaseOpenHelper.getWritableDatabase();
long rowId = db.insert(ContentLoaderConstant.TABLE_NAME, null, values);
if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(ContentLoaderConstant.CONTENT_ID_URI_BASE, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
/**
* 从数据库中删除数据,并notifyChange告诉CursorLoader加载数据。
*/
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mDataBaseOpenHelper.getWritableDatabase();
String finalWhere;
int count;
switch (mUriMatcher.match(uri)) {
case ContentLoaderConstant.FOCUS:
count = db.delete(ContentLoaderConstant.TABLE_NAME, where, whereArgs);
break;
case ContentLoaderConstant.FOCUS_ID:
finalWhere = DatabaseUtils.concatenateWhere(ContentLoaderConstant._ID + " = " + ContentUris.parseId(uri), where);
count = db.delete(ContentLoaderConstant.TABLE_NAME, finalWhere, whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
/**
* 更新数据库中的数据,并notifyChange告诉CursorLoader加载数据。
*/
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase db = mDataBaseOpenHelper.getWritableDatabase();
int count;
String finalWhere;
switch (mUriMatcher.match(uri)) {
case ContentLoaderConstant.FOCUS:
count = db.update(ContentLoaderConstant.TABLE_NAME, values, where, whereArgs);
break;
case ContentLoaderConstant.FOCUS_ID:
finalWhere = DatabaseUtils.concatenateWhere(ContentLoaderConstant._ID + " = " + ContentUris.parseId(uri), where);
count = db.update(ContentLoaderConstant.TABLE_NAME, values, finalWhere, whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
/**
* 从数据库中查询数据。
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(ContentLoaderConstant.TABLE_NAME);
switch (mUriMatcher.match(uri)) {
case ContentLoaderConstant.FOCUS :
qb.setProjectionMap(mNotesProjectionMap);
break;
case ContentLoaderConstant.FOCUS_ID :
qb.setProjectionMap(mNotesProjectionMap);
qb.appendWhere(ContentLoaderConstant._ID + "=?");
selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, new String[] {uri.getLastPathSegment()});
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (TextUtils.isEmpty(sortOrder)) {
sortOrder = ContentLoaderConstant.DEFAULT_SORT_ORDER;
}
SQLiteDatabase db = mDataBaseOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public String getType(Uri uri) {
switch (mUriMatcher.match(uri)) {
case ContentLoaderConstant.FOCUS :
return ContentLoaderConstant.CONTENT_TYPE;
case ContentLoaderConstant.FOCUS_ID :
return ContentLoaderConstant.CONTENT_ITEM_TYPE;
default :
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
}
(5)DataBaseHelper代码:
@Override
public void onCreate(SQLiteDatabase mSQLiteDatabase) {
mSQLiteDatabase.execSQL("CREATE TABLE "
+ ContentLoaderConstant.TABLE_NAME
+ " ("
+ ContentLoaderConstant._ID
+ " INTEGER PRIMARY KEY,"
+ ContentLoaderConstant.COLUMN_NAME_DATA
+ " TEXT"
+ ");");
}
@Override
public void onUpgrade(SQLiteDatabase mSQLiteDatabase, int oldVersion, int newVersion) {
mSQLiteDatabase.execSQL("DROP TABLE IF EXISTS notes");
onCreate(mSQLiteDatabase);
}
}
(6)常量文件代码:
private ContentLoaderConstant() {
}
public static final String DATABASE_NAME = "mayingcai.db";
public static final int DATABASE_VERSION = 1;
public static final String AUTHORITY = "com.focus.content.loader.Focus";
public static final String TABLE_NAME = "focus";
public static final String _ID = "_id";
public static final String COLUMN_NAME_DATA = "content";
public static final int FOCUS = 1;
public static final int FOCUS_ID = 2;
public static final String DEFAULT_SORT_ORDER = "content COLLATE LOCALIZED ASC";
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.focus.content.loader.Focus";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.focus.content.loader.Focus";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + TABLE_NAME);
public static final Uri CONTENT_ID_URI_BASE = Uri.parse("content://" + AUTHORITY + "/"+ TABLE_NAME +"/");
public static final String[] PROJECTION = new String[] {
_ID,
COLUMN_NAME_DATA,
};
}
(7)AndroidManifest.xml配置文件:
<application android:icon = "@drawable/icon" android:label = "@string/app_name">
<activity android:name = ".ContentLoaderActivity"
android:label = "@string/app_name"
>
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name = ".ContentLoaderProvider"
android:authorities = "com.focus.content.loader.Focus"
/>
</application>
<uses-sdk android:minSdkVersion = "11" />
</manifest>