cursoradpter自动更新
生活随笔
收集整理的這篇文章主要介紹了
cursoradpter自动更新
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
03 November 2014
Android Cursor自動更新的實現和原理
在Android日常開發中,時常會請求數據到Cursor,然后再通過Cursor獲取數據。像SQLiteDatabase和ContentProvider都使用了Cursor。在這些應用中,往往希望當數據發生改變時,Cursor也會自動的更新數據。這篇文章,我就會向你闡述如何通過Android自身的API實現Cursor的自動更新。另外我還將向你闡述這背后的原理。通過這些原理你可以舉一反三的實現更為廣泛的自動跟新。
文章中的代碼
可以在https://github.com/KOHOH1992/CursorSyncDemo中找到文章中出現的代碼
該項目共有4個分支。use_provider分支介紹了使用ContentProvider實現Cursor同步更新的方法。use_database分支介紹了不使用ContentProvider實現Cursor同步更新的方法。use_adapter分支介紹了不使用Loader實現Cursor同步更新的方法。
Cursor自動更新的實現
前提
首先假設項目使用了如下的前提
數據存儲在SqliteDataBase當中
通對ContentProvider的請求,獲取封裝了數據的Cursor
使用CursorLoader加載數據
使用AdapterView和CursorAdapter顯示數據
定義同步標志
static final Uri SYNC_SIGNAL_URI = Uri.parse(
? ? "content://com.kohoh.cursorsyncdemo/SYNC_SIGNAL");
在ContentProvider的query中設置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);
? ? //設置NotificationUri
? ? cursor.setNotificationUri(contentResolver, ContactContract.SYNC_SIGNAL_URI);
? ? return cursor;
}
在ContentProvider的insert,update,delete中觸發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) {
? ? ? ? //觸發NotificationUri
? ? ? ? contentResolver.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) {
? ? ? ? //觸發NotificationUri
? ? ? ? contentResolver.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) {
? ? //觸發NotificationUri
? ? contentResolver.notifyChange(ContactContract.SYNC_SIGNAL_URI, null);
? ? }
? ? return result;
}
Cursor的實現原理
Android的Cursor自動更新是通過觀察者模式實現的,整個過程如下圖所示
通過ContentPorvider和ContentResolver使得數據發生了改變
ContentProvider通知Cursor的觀察者數據發生了改變
Cursor通知CursorLoader的觀察者數據發生了改變
CursorLoader通過ContentProvider加載新的數據
ContentPovider向DataBase請求新的數據
CursorLoader調用CursorAdapter#changeCursor,用封裝了新數據的Cursor替換舊的Cursor
CursorAdapter告知AdapterView的觀察者有新的數據
AdapterView重新加載并顯示數據
在Android的android.database包下,有一個ContentObserver。Android正是通過他來實現觀察者模式的。當數據改變之后,觀察者會將數據改變的消息通知相應的對象,進而做出反饋。在代碼中,當數據改變之后,我會調用ContentResolver#notifyChange,發出ContactContract.SYNC_SIGNAL_URI信號,通知數據發生了改變。而在此之前,從ContentProvider#query中獲得的Cursor已經通過Cursor#setNotificationUri對ContactContract.SYNC_SIGNAL_URI信號進行了監視。當該信號出現,Cursor就會將信息改變的消息告訴CursorLoader的觀察者(在此之前CursorLoader已經對該Cursor設立了觀察者)。CursorLoader會開始重新開始加載數據。當數據加載成功,CursorLoader會通過CursorAdapter#changeCursor設置封裝了新數據的Cursor。而后CursorAdapter又會通知AdapterView的觀察者數據發生了改變(在此之前AdapterView已經對CursorAdapter設立了觀察者)。最后AdapterView就會重新加載并顯示新的數據。
在整個過程當中,我要做的就是在改變數據時發出信號,對封裝數據的Cursor設置需要監視的信號。具體的說就是在query中調用Cursor#setNotificationUri,在insert、update、delete中調用ContentResolver#notifyChange。這里需要補充的是Cursor和ContentResolver的信號機制同樣是通過觀察者模式實現的。
其他的實現方式
這里要介紹的其他的實現方式,依舊是通過觀察者模式實現的。區別在于是否使用ContentProvider和CursorLoader
不使用ContentProvider
在開發過程中,如果數據不用于應用之間的共享,使用ContentProvider似乎有一些多余。然而Android提供的CursorLoader的API必須通過ContentProvider才能實現數據加載和數據同步更新。但是你任然可以在不使用ContentProvider的情況下實現Cursor的自動更新。你需要做的只是在你的Loader中加入下面的代碼
// 實例化一個全局的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();
? ? ? ? // 對Cursor設立觀察者
? ? ? ? cursor.registerContentObserver(mObserver);
? ? ? ? // 設置Cursor的觀察信號
? ? ? ? cursor.setNotificationUri(getContext().getContentResolver(),?
? ? ? ? ? ? mNotificationUri);
? ? }
? ? return cursor;
}
ForceLoadContentObserver是Loader的內部類。當觀察到數據發生變化之后,該類會調用Loader#forceLoad,進而開始重新加載數據。另外你也可以直接使用我項目中的DatabaseLoader。該類是我參照CursorLoader編寫的一個工具,通過它你可以繞過ContentProvider,直接請求Database。
不使用Loader
如果你不想要使用Loader(我非常不贊成你這么做),你可以通過如下的代碼實現Cursor的同步更新。
// 使用CursorAdapter.FLAG_AUTO_REQUERY標志
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);
? ? //設置NotificationUri
? ? cursor.setNotificationUri(this.getContentResolver(),?
? ? ? ? ContactContract.SYNC_SIGNAL_URI);
? ? adapter.changeCursor(cursor);
}
這里的關鍵在于,在實例化CursorAdapter時使用了CursorAdapter.FLAGAUTOREQUERY標志。當使用該標志后,每當收到數據更新的消息,CursorAdapter就會自己調用CursorAdapter#requery重新加載數據。然而整個加載過程會再UI線程中發生,這很有可能會使得程序運行部流暢。正是因為這個原因該方法以及被Android設置為Deprecated了。因此如果有可能,我還是推薦你使用Loader。
就是這樣!!
總結
以上是生活随笔為你收集整理的cursoradpter自动更新的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Indetifier
- 下一篇: 7.5 obtaining datab