Android Cursor自动更新的实现和原理
原文鏈接:http://www.sxrczx.com/pages/kohoh1992.github.io/cursor-auto-sync/index_1431878338570.html
在Android日常開(kāi)發(fā)中,時(shí)常會(huì)請(qǐng)求數(shù)據(jù)到Cursor,然后再通過(guò)Cursor獲取數(shù)據(jù)。像SQLiteDatabase和ContentProvider都使用了Cursor。在這些應(yīng)用中,往往希望當(dāng)數(shù)據(jù)發(fā)生改變時(shí),Cursor也會(huì)自動(dòng)的更新數(shù)據(jù)。這篇文章,我就會(huì)向你闡述如何通過(guò)Android自身的API實(shí)現(xiàn)Cursor的自動(dòng)更新。另外我還將向你闡述這背后的原理。通過(guò)這些原理你可以舉一反三的實(shí)現(xiàn)更為廣泛的自動(dòng)跟新。
文章中的代碼
可以在https://github.com/KOHOH1992/CursorSyncDemo中找到文章中出現(xiàn)的代碼
該項(xiàng)目共有4個(gè)分支。use_provider分支介紹了使用ContentProvider實(shí)現(xiàn)Cursor同步更新的方法。use_database分支介紹了不使用ContentProvider實(shí)現(xiàn)Cursor同步更新的方法。use_adapter分支介紹了不使用Loader實(shí)現(xiàn)Cursor同步更新的方法。
Cursor自動(dòng)更新的實(shí)現(xiàn)
前提
首先假設(shè)項(xiàng)目使用了如下的前提
- 數(shù)據(jù)存儲(chǔ)在SqliteDataBase當(dāng)中
- 通對(duì)ContentProvider的請(qǐng)求,獲取封裝了數(shù)據(jù)的Cursor
- 使用CursorLoader加載數(shù)據(jù)
- 使用AdapterView和CursorAdapter顯示數(shù)據(jù)
定義同步標(biāo)志
static final Uri SYNC_SIGNAL_URI = Uri.parse("content://com.kohoh.cursorsyncdemo/SYNC_SIGNAL");在ContentProvider的query中設(shè)置NotificationUri
@Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {SQLiteDatabase database = sqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(ContactContract.CONTACT_TABLE, projection,selection,selectionArgs, null, null, sortOrder);//設(shè)置NotificationUricursor.setNotificationUri(contentResolver, ContactContract.SYNC_SIGNAL_URI);return cursor; }在ContentProvider的insert,update,delete中觸發(fā)NotificationUri
@Override public Uri insert(Uri uri, ContentValues values) {SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();long id = database.insert(ContactContract.CONTACT_TABLE, null, values);if (id >= 0) {//觸發(fā)NotificationUricontentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return uri.withAppendedPath(ContactContract.CONTACT_URI, String.valueOf(id)); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();int result = database.update(ContactContract.CONTACT_TABLE, values, selection, selectionArgs);if (result > 0) {//觸發(fā)NotificationUricontentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return result; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) {SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();int result = database.delete(ContactContract.CONTACT_TABLE, selection, selectionArgs);if (result > 0) {//觸發(fā)NotificationUricontentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return result; }CursorLoader
ForceLoadContentObserver mObserver;public Cursor loadInBackground() {...try {//不過(guò)多解釋,耗時(shí)的查詢操作Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,mSelectionArgs, mSortOrder, mCancellationSignal);if (cursor != null) {try {// Ensure the cursor window is filled.cursor.getCount();//給Cursor設(shè)置觀察者;ContentProvider通知Cursor的觀察者數(shù)據(jù)發(fā)生了改變,//Cursor通知CursorLoader的觀察者數(shù)據(jù)發(fā)生了改變,CursorLoader通過(guò)ContentProvider重新加載新的數(shù)據(jù)cursor.registerContentObserver(mObserver);//cursor.setNotificationUri(getContext().getContentResolver(), otificationUri);//給Cursor設(shè)置要觀察的URI} catch (RuntimeException ex) {cursor.close();throw ex;}}return cursor;} }Cursor的實(shí)現(xiàn)原理
Android的Cursor自動(dòng)更新是通過(guò)觀察者模式實(shí)現(xiàn)的,整個(gè)過(guò)程如下圖所示
- 通過(guò)ContentPorvider和ContentResolver使得數(shù)據(jù)發(fā)生了改變
- ContentProvider通知Cursor的觀察者數(shù)據(jù)發(fā)生了改變
- Cursor通知CursorLoader的觀察者數(shù)據(jù)發(fā)生了改變
- CursorLoader通過(guò)ContentProvider加載新的數(shù)據(jù)
- ContentPovider向DataBase請(qǐng)求新的數(shù)據(jù)
- CursorLoader調(diào)用CursorAdapter# changeCursor,用封裝了新數(shù)據(jù)的Cursor替換舊的Cursor
- CursorAdapter告知AdapterView的觀察者有新的數(shù)據(jù)
- AdapterView重新加載并顯示數(shù)據(jù)
在Android的android.database包下,有一個(gè)ContentObserver。Android正是通過(guò)他來(lái)實(shí)現(xiàn)觀察者模式的。當(dāng)數(shù)據(jù)改變之后,觀察者會(huì)將數(shù)據(jù)改變的消息通知相應(yīng)的對(duì)象,進(jìn)而做出反饋。在代碼中,當(dāng)數(shù)據(jù)改變之后,我會(huì)調(diào)用ContentResolver# notifyChange,發(fā)出ContactContract.SYNC_SIGNAL_URI信號(hào),通知數(shù)據(jù)發(fā)生了改變。而在此之前,從ContentProvider# query中獲得的Cursor已經(jīng)通過(guò)Cursor# setNotificationUri對(duì)ContactContract.SYNC_SIGNAL_URI信號(hào)進(jìn)行了監(jiān)視。當(dāng)該信號(hào)出現(xiàn),Cursor就會(huì)將信息改變的消息告訴CursorLoader的觀察者(在此之前CursorLoader已經(jīng)對(duì)該Cursor設(shè)立了觀察者)。CursorLoader會(huì)開(kāi)始重新開(kāi)始加載數(shù)據(jù)。當(dāng)數(shù)據(jù)加載成功,CursorLoader會(huì)通過(guò)CursorAdapter# changeCursor設(shè)置封裝了新數(shù)據(jù)的Cursor。而后CursorAdapter又會(huì)通知AdapterView的觀察者數(shù)據(jù)發(fā)生了改變(在此之前AdapterView已經(jīng)對(duì)CursorAdapter設(shè)立了觀察者)。最后AdapterView就會(huì)重新加載并顯示新的數(shù)據(jù)。
在整個(gè)過(guò)程當(dāng)中,我要做的就是在改變數(shù)據(jù)時(shí)發(fā)出信號(hào),對(duì)封裝數(shù)據(jù)的Cursor設(shè)置需要監(jiān)視的信號(hào)。具體的說(shuō)就是在query中調(diào)用Cursor# setNotificationUri,在insert、update、delete中調(diào)用ContentResolver# notifyChange。這里需要補(bǔ)充的是Cursor和ContentResolver的信號(hào)機(jī)制同樣是通過(guò)觀察者模式實(shí)現(xiàn)的。
其他的實(shí)現(xiàn)方式
這里要介紹的其他的實(shí)現(xiàn)方式,依舊是通過(guò)觀察者模式實(shí)現(xiàn)的。區(qū)別在于是否使用ContentProvider和CursorLoader
不使用ContentProvider
在開(kāi)發(fā)過(guò)程中,如果數(shù)據(jù)不用于應(yīng)用之間的共享,使用ContentProvider似乎有一些多余。然而Android提供的CursorLoader的API必須通過(guò)ContentProvider才能實(shí)現(xiàn)數(shù)據(jù)加載和數(shù)據(jù)同步更新。但是你任然可以在不使用ContentProvider的情況下實(shí)現(xiàn)Cursor的自動(dòng)更新。你需要做的只是在你的Loader中加入下面的代碼
// 實(shí)例化一個(gè)全局的ForceLoadContentObserver ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); @Override public Cursor loadInBackground() {SQLiteDatabase database = mSqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(mTable, mColumns, mSelection, mSelectionArgs, mGroupBy,mHaving, mOrderBy);if (cursor != null) {cursor.getCount();// 對(duì)Cursor設(shè)立觀察者cursor.registerContentObserver(mObserver);// 設(shè)置Cursor的觀察信號(hào)cursor.setNotificationUri(getContext().getContentResolver(), mNotificationUri);}return cursor; }ForceLoadContentObserver是Loader的內(nèi)部類。當(dāng)觀察到數(shù)據(jù)發(fā)生變化之后,該類會(huì)調(diào)用Loader# forceLoad,進(jìn)而開(kāi)始重新加載數(shù)據(jù)。另外你也可以直接使用我項(xiàng)目中的DatabaseLoader。該類是我參照CursorLoader編寫的一個(gè)工具,通過(guò)它你可以繞過(guò)ContentProvider,直接請(qǐng)求Database。
不使用Loader
如果你不想要使用Loader(我非常不贊成你這么做),你可以通過(guò)如下的代碼實(shí)現(xiàn)Cursor的同步更新。
// 使用CursorAdapter.FLAG_AUTO_REQUERY標(biāo)志 adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to,CursorAdapter.FLAG_AUTO_REQUERY); private void loadData() {SQLiteOpenHelper sqliteOpenHelper = ContactContract.getSqliteOpenHelper(this);SQLiteDatabase database = sqliteOpenHelper.getReadableDatabase();String[] columns = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE};Cursor cursor = database.query(ContactContract.CONTACT_TABLE, columns, null, null, null,null, null);//設(shè)置NotificationUricursor.setNotificationUri(this.getContentResolver(), ContactContract.SYNC_SIGNAL_URI);adapter.changeCursor(cursor); }這里的關(guān)鍵在于,在實(shí)例化CursorAdapter時(shí)使用了CursorAdapter.FLAGAUTOREQUERY標(biāo)志。當(dāng)使用該標(biāo)志后,每當(dāng)收到數(shù)據(jù)更新的消息,CursorAdapter就會(huì)自己調(diào)用CursorAdapter# requery重新加載數(shù)據(jù)。然而整個(gè)加載過(guò)程會(huì)再UI線程中發(fā)生,這很有可能會(huì)使得程序運(yùn)行部流暢。正是因?yàn)檫@個(gè)原因該方法以及被Android設(shè)置為Deprecated了。因此如果有可能,我還是推薦你使用Loader。
ContactContract.java
package com.kohoh.cursorsyncdemo;import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.provider.BaseColumns;import com.google.common.base.Preconditions; import com.google.common.base.Strings;/*** Created by kohoh on 14-11-3.*/ public class ContactContract implements BaseColumns {static final int DATABSE_VERSION = 1;static final String DATABASE_NAME = "contact.db";static final String CONTACT_TABLE = "contact";static final String NAME = "name";static final String PHONE = "phone";static final String AUTHORITY = "com.kohoh.cursorsyncdemo";static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY);static final Uri CONTACT_URI = Uri.withAppendedPath(BASE_URI, "contact");static final Uri SYNC_SIGNAL_URI = Uri.withAppendedPath(BASE_URI, "SYNC_SIGNAL_URI");static public ContactDatabaseHelper getSqliteOpenHelper(Context context) {return new ContactDatabaseHelper(context);}static class ContactDatabaseHelper extends SQLiteOpenHelper {public ContactDatabaseHelper(Context context) {super(context, DATABASE_NAME, null, DATABSE_VERSION);}static public long addContact(SQLiteDatabase database, String name, int phone) {Preconditions.checkNotNull(database);Preconditions.checkNotNull(phone);Preconditions.checkArgument(!Strings.isNullOrEmpty(name));ContentValues contentValues = new ContentValues();contentValues.put(NAME, name);contentValues.put(PHONE, phone);return database.insert(CONTACT_TABLE, null, contentValues);}static public void deleteContact(Context context, long id) {Preconditions.checkNotNull(context);Preconditions.checkArgument(id >= 0);ContactContract.ContactDatabaseHelper databaseHelper = ContactContract.getSqliteOpenHelper(context);SQLiteDatabase databasea = databaseHelper.getWritableDatabase();String where = ContactContract._ID + " = ?";String[] whereArgs = {String.valueOf(id)};databasea.delete(ContactContract.CONTACT_TABLE, where, whereArgs);context.getContentResolver().notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE " + CONTACT_TABLE + "( " +_ID + " INTEGER PRIMARY KEY," +NAME + " TEXT," +PHONE + " INTERGER)");addContact(db, "aaa", 111);addContact(db, "bbb", 222);addContact(db, "ccc", 333);addContact(db, "ddd", 444);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}} }ContactProvider.java
package com.kohoh.cursorsyncdemo;import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri;/*** Created by kohoh on 14-11-3.*/ public class ContactProvider extends ContentProvider {private SQLiteOpenHelper sqLiteOpenHelper;private ContentResolver contentResolver;private UriMatcher uriMatcher;final private int DIR = 0;final private int ITEM = 1;@Overridepublic boolean onCreate() {sqLiteOpenHelper = ContactContract.getSqliteOpenHelper(getContext());contentResolver = getContext().getContentResolver();uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);uriMatcher.addURI(ContactContract.AUTHORITY, "contact", DIR);uriMatcher.addURI(ContactContract.AUTHORITY, "contact/#", ITEM);return true;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder) {if (uriMatcher.match(uri) == ITEM) {return null;}SQLiteDatabase database = sqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(ContactContract.CONTACT_TABLE, projection, selection,selectionArgs, null, null, sortOrder);cursor.setNotificationUri(contentResolver, ContactContract.SYNC_SIGNAL_URI);return cursor;}@Overridepublic String getType(Uri uri) {switch (uriMatcher.match(uri)) {case ITEM:return "vnd.android.cursor.item/vnd.con.kohoh.cursorsyncdemo";case DIR:return "vnd.android.cursor.dir/vnd.con.kohoh.cursorsyncdemo";default:return null;}}@Overridepublic Uri insert(Uri uri, ContentValues values) {if (uriMatcher.match(uri) == ITEM) {return null;}SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();long id = database.insert(ContactContract.CONTACT_TABLE, null, values);if (id >= 0) {contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return uri.withAppendedPath(ContactContract.CONTACT_URI, String.valueOf(id));}@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {if (uriMatcher.match(uri) == ITEM) {return 0;}SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();int result = database.delete(ContactContract.CONTACT_TABLE, selection, selectionArgs);if (result > 0) {contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return result;}@Overridepublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {if (uriMatcher.match(uri) == ITEM) {return 0;}SQLiteDatabase database = sqLiteOpenHelper.getWritableDatabase();int result = database.update(ContactContract.CONTACT_TABLE, values, selection, selectionArgs);if (result > 0) {contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);}return result;} }DatabaseLoader.java
package com.kohoh.cursorsyncdemo;import android.content.AsyncTaskLoader; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri;/*** Created by kohoh on 14-11-3.*/ public class DatabaseLoader extends AsyncTaskLoader<Cursor> {final ForceLoadContentObserver mObserver;Uri mNotificationUri;String mTable;String[] mColumns;String mSelection;String[] mSelectionArgs;String mGroupBy;String mHaving;String mOrderBy;SQLiteOpenHelper mSqLiteOpenHelper;Cursor mCursor;public DatabaseLoader(Context context) {super(context);this.mObserver = new ForceLoadContentObserver();}public DatabaseLoader(Context context, SQLiteOpenHelper sqLiteOpenHelper, Uri mNotificationUri,String mTable, String[] mColumns, String mSelection, String[] mSelectionArgs,String mGroupBy, String mHaving, String mOrderBy) {super(context);this.mNotificationUri = mNotificationUri;this.mTable = mTable;this.mColumns = mColumns;this.mSelection = mSelection;this.mSelectionArgs = mSelectionArgs;this.mGroupBy = mGroupBy;this.mHaving = mHaving;this.mOrderBy = mOrderBy;this.mSqLiteOpenHelper = sqLiteOpenHelper;this.mObserver = new ForceLoadContentObserver();}@Overridepublic Cursor loadInBackground() {SQLiteDatabase database = mSqLiteOpenHelper.getReadableDatabase();Cursor cursor = database.query(mTable, mColumns, mSelection, mSelectionArgs, mGroupBy,mHaving, mOrderBy);if (cursor != null) {cursor.getCount();cursor.registerContentObserver(mObserver);cursor.setNotificationUri(getContext().getContentResolver(), mNotificationUri);}return cursor;}@Overridepublic void deliverResult(Cursor cursor) {if (isReset()) {if (cursor != null) {cursor.close();}return;}Cursor oldCursor = mCursor;mCursor = cursor;if (isStarted()) {super.deliverResult(cursor);}if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {oldCursor.close();}}@Overrideprotected void onStartLoading() {if (mCursor != null) {deliverResult(mCursor);}if (takeContentChanged() || mCursor == null) {forceLoad();}}@Overrideprotected void onStopLoading() {// Attempt to cancel the current load task if possible.cancelLoad();}@Overridepublic void onCanceled(Cursor cursor) {if (cursor != null && !cursor.isClosed()) {cursor.close();}}@Overrideprotected void onReset() {super.onReset();onStopLoading();if (mCursor != null && !mCursor.isClosed()) {mCursor.close();}mCursor = null;}public SQLiteOpenHelper getSqLiteOpenHelper() {return mSqLiteOpenHelper;}public void setSqLiteOpenHelper(SQLiteOpenHelper mSqLiteOpenHelper) {this.mSqLiteOpenHelper = mSqLiteOpenHelper;}public Uri getNotificationUri() {return mNotificationUri;}public void setNotificationUri(Uri mNotificationUri) {this.mNotificationUri = mNotificationUri;}public String getTable() {return mTable;}public void setTable(String mTable) {this.mTable = mTable;}public String[] getColumns() {return mColumns;}public void setColumns(String[] mColumns) {this.mColumns = mColumns;}public String getSelection() {return mSelection;}public void setSelection(String mSelection) {this.mSelection = mSelection;}public String[] getSelectionArgs() {return mSelectionArgs;}public void setSelectionArgs(String[] mSelectionArgs) {this.mSelectionArgs = mSelectionArgs;}public String getGroupBy() {return mGroupBy;}public void setGroupBy(String mGroupBy) {this.mGroupBy = mGroupBy;}public String getHaving() {return mHaving;}public void setHaving(String mHaving) {this.mHaving = mHaving;}public String getOrderBy() {return mOrderBy;}public void setOrderBy(String mOrderBy) {this.mOrderBy = mOrderBy;} }use provider
package com.kohoh.cursorsyncdemo;import android.app.Activity; import android.app.LoaderManager; import android.content.CursorLoader; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter;public class CursorSyncDemo extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {private ListView listView;private SimpleCursorAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_cursor_sync_demo);String[] from = {ContactContract.NAME, ContactContract.PHONE};int[] to = {R.id.name, R.id.phone};listView = (ListView) findViewById(R.id.lv);adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to,CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);listView.setAdapter(adapter);getLoaderManager().initLoader(0, null, this);registerForContextMenu(listView);}@Overridepublic boolean onContextItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.delete:AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();ContactContract.ContactDatabaseHelper.deleteContact(this, menuInfo.id);return true;default:return super.onContextItemSelected(item);}}@Overridepublic void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.contact_item_menu, menu);}@Overridepublic Loader<Cursor> onCreateLoader(int id, Bundle args) {String[] projection = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE};return new CursorLoader(this, ContactContract.CONTACT_URI, projection, null, null, null);}@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor data) {adapter.changeCursor(data);}@Overridepublic void onLoaderReset(Loader<Cursor> loader) {} }use database
package com.kohoh.cursorsyncdemo;import android.app.Activity; import android.app.LoaderManager; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter;public class CursorSyncDemo extends Activity implements LoaderManager.LoaderCallbacks<Cursor> {private ListView listView;private SimpleCursorAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_cursor_sync_demo);String[] from = {ContactContract.NAME, ContactContract.PHONE};int[] to = {R.id.name, R.id.phone};listView = (ListView) findViewById(R.id.lv);adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to,CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);listView.setAdapter(adapter);getLoaderManager().initLoader(0, null, this);registerForContextMenu(listView);}@Overridepublic Loader<Cursor> onCreateLoader(int id, Bundle args) {String[] columns = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE};return new DatabaseLoader(this,ContactContract.getSqliteOpenHelper(this),ContactContract.SYNC_SIGNAL_URI,ContactContract.CONTACT_TABLE,columns,null,null,null,null,null);}@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor data) {adapter.changeCursor(data);}@Overridepublic void onLoaderReset(Loader<Cursor> loader) {}@Overridepublic boolean onContextItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.delete:AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();ContactContract.ContactDatabaseHelper.deleteContact(this, menuInfo.id);return true;default:return super.onContextItemSelected(item);}}@Overridepublic void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.contact_item_menu, menu);} }use adapter
package com.kohoh.cursorsyncdemo;import android.app.Activity; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Bundle; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.SimpleCursorAdapter;public class CursorSyncDemo extends Activity {private ListView listView;private SimpleCursorAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_cursor_sync_demo);String[] from = {ContactContract.NAME, ContactContract.PHONE};int[] to = {R.id.name, R.id.phone};listView = (ListView) findViewById(R.id.lv);adapter = new SimpleCursorAdapter(this, R.layout.contact_item, null, from, to,CursorAdapter.FLAG_AUTO_REQUERY);listView.setAdapter(adapter);loadData();registerForContextMenu(listView);}private void loadData() {SQLiteOpenHelper sqliteOpenHelper = ContactContract.getSqliteOpenHelper(this);SQLiteDatabase database = sqliteOpenHelper.getReadableDatabase();String[] columns = {ContactContract._ID, ContactContract.NAME, ContactContract.PHONE};Cursor cursor = database.query(ContactContract.CONTACT_TABLE, columns, null, null, null,null, null);cursor.setNotificationUri(this.getContentResolver(), ContactContract.SYNC_SIGNAL_URI);adapter.changeCursor(cursor);}@Overridepublic boolean onContextItemSelected(MenuItem item) {switch (item.getItemId()) {case R.id.delete:AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();ContactContract.ContactDatabaseHelper.deleteContact(this, menuInfo.id);return true;default:return super.onContextItemSelected(item);}}@Overridepublic void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {MenuInflater inflater = getMenuInflater();inflater.inflate(R.menu.contact_item_menu, menu);} }總結(jié)
以上是生活随笔為你收集整理的Android Cursor自动更新的实现和原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: swapCursor vs change
- 下一篇: Android键盘面板冲突 布局闪动处理