1. 概念:
在HoneyComb3.0中提出了新的异步数据加载特性(ContentLoader),它可以异步的从后台获取数据并更新UI界面。CursorLoader类是专门为此
特性设计的,他允许程序通过ContentProvider异步的从数据中读取数据,并将获取的数据显示到UI界面中。
2. 效果图:
(1)主操作界面,在Action|Bar中存在两个"快捷按钮",如下图:
(2)单击"加载按钮"后从数据库中动态的加载数据(当前数据库只添加了一条数据),如下图:
(3)间隔5秒后继续从数据库中读取数据(数据库被动态的添加了数据,并调用ContentResovler的notifiyChange()方法,该方法不调5秒后不
会加载数据):
3. 功能实现:
(1)主Activity代码:package com.focus.content.loader; import android.app.Activity; import android.app.FragmentManager; import android.os.Bundle; /** * * ContentLoader实例的入口Activity类。 * * @author 马英才 * */ public class ContentLoaderActivity extends 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代码:package com.focus.content.loader; import android.app.ListFragment; import android.content.ContentResolver; import android.content.ContentValues; import android.os.AsyncTask; import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.SimpleCursorAdapter; /** * * 显示从数据库中查询出的内容的Fragment,它是ListFragment,即:Fragment中包含一个ListView组件。 * * @author 马英才 * */ public class ContentLoaderFragment extends ListFragment { static final int POPULATE_ID = Menu.FIRST; static final int CLEAR_ID = Menu.FIRST+1; private SimpleCursorAdapter mSimpleCursorAdapter; private AsyncTask<Void, Void, Void> mPopulatingTask; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setHasOptionsMenu(true); /** * 渲染Adapter,即:给ListFragment中的ListView设置的Adapter实现。 */ mSimpleCursorAdapter = new SimpleCursorAdapter ( getActivity(), android.R.layout.simple_list_item_1, null, new String[] { ContentLoaderConstant.COLUMN_NAME_DATA }, new int[] { android.R.id.text1 }, 0 ); /** * 给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回调方法:package com.focus.content.loader; import android.app.LoaderManager; import android.content.Context; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.widget.SimpleCursorAdapter; /** * * LoaderCallbacks实现,创建CursorLoader,根据设置的更新时间,异步的从后台数据库搜索数据,并通过Adapter更新显示列表。 * * @author 马英才 * */ public class ContentLoaderCallBack implements LoaderManager.LoaderCallbacks<Cursor> { private Context mContext; 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代码:package com.focus.content.loader; import java.util.HashMap; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; /** * * ContentProvider实现,对数据库执行增,删,改,查操作。 * * @author 马英才。 * */ public class ContentLoaderProvider extends ContentProvider { private final UriMatcher mUriMatcher; 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代码:package com.focus.content.loader; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DataBaseHelper extends SQLiteOpenHelper { public DataBaseHelper(Context context) { super(context, ContentLoaderConstant.DATABASE_NAME, null, ContentLoaderConstant.DATABASE_VERSION); } @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)常量文件代码:package com.focus.content.loader; import android.net.Uri; public class ContentLoaderConstant { 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配置文件:<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android = "http://schemas.android.com/apk/res/android" package = "com.focus.content.loader" android:versionCode = "1" android:versionName = "1.0" > <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>