1. 程式人生 > >Android數據庫內容變化的監聽

Android數據庫內容變化的監聽

pda lose match vat method dea ren pac rev

首先介紹內容監測的基本模式
基於uri的內容監測的基本模式被android.content.ContentResolver實現
它為基於Uri的內容監測的提供了一個平臺。(其實如果有必要,我們可以自己實現一個)
ContentResolver為此提供了三個方法:
註冊監聽器到某個uri
public final void registerContentObserver (Uri uri, boolean notifyForDescendents, ContentObserver observer)
Register an observer class that gets callbacks when data identified by a given content URI changes.
Parameters
uri The URI to watch for changes. This can be a specific row URI, or a base URI for a whole class of content.
notifyForDescendents If true changes to URIs beginning with uri will also cause notifications to be sent.
If false only changes to the exact URI specified by uri will cause notifications to be sent.
If true, than any URI values at or below the specified URI will also trigger a match.
observer The object that receives callbacks when changes occur.
取消被註冊的監聽器

public final void unregisterContentObserver (ContentObserver observer)
Unregisters a change observer.
參數
observer The previously registered observer that is no longer needed.
通知所有註冊到uri的監聽器,告訴他們內容發生了改變。
public void notifyChange (Uri uri, ContentObserver observer)
Notify registered observers that a row was updated. To register, call registerContentObserver(). By default, CursorAdapter objects will get this notification.
參數
observer The observer that originated the change, may be null .用以說明observer
是第一個發現內容改變的ContentObserver,可為null.
通知所有註冊到uri的監聽器,告訴他們內容發生了改變。
public void notifyChange (Uri uri, ContentObserver observer, boolean syncToNetwork)
Notify registered observers that a row was updated. To register, call registerContentObserver(). By default, CursorAdapter objects will get this notification.
參數
observer The observer that originated the change, may be null 用以說明observer
是第一個發現內容改變的ContentObserver,可為null
syncToNetwork If true, attempt to sync the change to the network.
註1:"基於uri的內容監測的基本模式被android.content.ContentResolver實現",嚴格來說ContentResolver至少提供了接口。
真正的實現在android.content.ContentService.
其實ContentResolver為基於Uri的內容監測所提供的方法只是調用ContentService相關的方法
註2:"註冊監聽器到uri"只是說如果notifyChangeuriregisterContentObserver中的uri相匹配,則調用observer的方法onChange
內容監聽器ContentObserver主要有三個方法
public boolean deliverSelfNotifications ()
Returns true if this observer is interested in notifications for changes made through the cursor the observer is registered with.
註:這個函數的使用還是puzzle.
public final void dispatchChange (boolean selfChange)
註:這個是為提供用handler執行onChange的一個接口。所以一般比沒必要重載它。
public void onChange (boolean selfChange)
This method is called when a change occurs to the cursor that is being observed.
參數
selfChange true if the update was caused by a call to commit on the cursor that is being observed.
註1:這個就是我們需要重載的函數,在裏面可以實現對內容改變的個性化響應。
關於ContentObserver的更多內容請參閱《內容監聽器ContentObserver》。
基本使用是這樣的:
首先使用registerContentObserver註冊observer監聽器到uri,然後在內容發生改變時,就調用notifyChange通知系統所有註冊到該uri的監聽器,告訴他們內容發生了改變。
這時相應的監聽器的dispatchChange方法被調用,在dispatchChange中onChange被調用。
最後如果不想讓observer監聽器uri相關的內容監聽,可以調用unregisterContentObserver來取消註冊。

對數據庫改變的監測是如何實現的呢?
在providers對數據進行改變後,會通過getContext().getContentResolver().notifyChange的發生相應的uri的內容發生了改變
比如:com.android.providers.contacts中的ContactsProvider2
ContactsProvider2.java文件
public class ContactsProvider2 extends SQLiteContentProvider implements OnAccountsUpdateListener {
-------------------省略------------------
@Override
public Uri insert(Uri uri, ContentValues values) {
waitForAccess();
return super.insert(uri, values);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-------------------省略------------------
waitForAccess();
return super.update(uri, values, selection, selectionArgs);
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
waitForAccess();
return super.delete(uri, selection, selectionArgs);
}
}
-------------------省略------------------
protected void notifyChange() {
notifyChange(mSyncToNetwork);
mSyncToNetwork = false;
}

protected void notifyChange(boolean syncToNetwork) {
getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
syncToNetwork);
}
-------------------省略------------------
}
SQLiteContentProvider.java文件
public abstract class SQLiteContentProvider extends ContentProvider
implements SQLiteTransactionListener {
@Override
public Uri insert(Uri uri, ContentValues values) {
Uri result = null;
boolean applyingBatch = applyingBatch();
if (!applyingBatch) {
mDb = mOpenHelper.getWritableDatabase();
mDb.beginTransactionWithListener(this);
try {
result = insertInTransaction(uri, values);
if (result != null) {
mNotifyChange = true;
}
mDb.setTransactionSuccessful();
} finally {
mDb.endTransaction();
}

onEndTransaction();
} else {
result = insertInTransaction(uri, values);
if (result != null) {
mNotifyChange = true;
}
}
return result;
}
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-------------------省略------------------
}
public int delete(Uri uri, String selection, String[] selectionArgs) {
-------------------省略------------------
}
-------------------省略------------------
protected abstract void notifyChange();
-------------------省略------------------
protected void onEndTransaction() {
if (mNotifyChange) {
mNotifyChange = false;
notifyChange();
}
}
-------------------省略------------------
}
那麽Cursor是如何實現對基於uri的數據庫內容變化進行監聽呢?
只要把一個監聽器ContentObserver對uri使用ContentResolver的
registerContentObserver方法進行註冊。
如果相應
uri的數據庫內容變化發變化,先通知ContentObserver,再又讓它通知Cursor。
當然為了方便系統把ContentObserver設計成了內部類
具體可見在Cursor接口的一個實現android.database.AbstractCursor
AbstractCursor.java文件請見附件1
同時Cursor為我們提供了setNotificationUri(ContentResolver cr, Uri notifyUri)讓它內部類的ContentObserver註冊到ContentResolver的某個Uri上.
具體代碼參見附件1。
那麽外部如何監聽Cursor呢?。
Cursor為外部監測數據庫的變化提供了以下接口:
abstract void registerContentObserver(ContentObserver observer)
Register an observer that is called when changes happen to the content backing this cursor.
註1:當數據庫內容變化時通知observer,具體見附件1的onChange(boolean selfChange)函數.
註2:這個監聽器observer主要是在setNotificationUri(ContentResolver cr, Uri notifyUri)中notifyUri對應的數據庫內容發生變化時被通知的(間接),
abstract void unregisterContentObserver(ContentObserver observer)
Unregister an observer that has previously been registered with this cursor via registerContentObserver(ContentObserver).
另外,Cursor還為我們提供了接口,讓外部可以通過註冊一個DataSetObserver來對Cursor的requery(), deactivate(), close()行為進行監聽。 abstract void registerDataSetObserver(DataSetObserver observer)
Register an observer that is called when changes happen to the contents of the this cursors data set, for example, when the data set is changed via requery(), deactivate(), or close().
註1:當Cursor發生requery(), deactivate(), or close()時通知DataSetObserver observer abstract void unregisterDataSetObserver(DataSetObserver observer)
Unregister an observer that has previously been registered with this cursor via registerContentObserver(ContentObserver). DataSetObserver的主要方法
Public Methods
void onChanged() This method is called when the entire data set has changed, most likely through a call to requery() on a Cursor.
void onInvalidated() This method is called when the entire data becomes invalid, most likely through a call to deactivate() or close() on a Cursor.
那麽CursorAdapter是如何實現對基於uri的數據庫內容變化進行監聽呢?
它當然是借助Cursor來實現的。
但是CursorAdapterContentObserver相對應對外提供(可以重載)的接口是:
onContentChanged()
當時CursorAdapterDataSetObserver相對應對外提供(可以重載)的接口是:
onChanged()->notifyDataSetChanged()//這個消息並完全不是從Cursor傳遞來,它會在CursorAdapter內手動被調用
onInvalidated()->notifyDataSetInvalidated()//這個消息並不是從Cursor傳遞來。
具體可以參照附件2的android.widget.CursorAdapter源代碼
CursorAdapternotifyDataSetChanged()notifyDataSetInvalidated()事件可以繼續往外傳遞 BaseAdapter為此提供了以下系列方法。
void notifyDataSetChanged() Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.
void notifyDataSetInvalidated() Notifies the attached observers that the underlying data is no longer valid or available.
void registerDataSetObserver(DataSetObserver observer) Register an observer that is called when changes happen to the data used by this adapter.
void unregisterDataSetObserver(DataSetObserver observer) Unregister an observer that has previously been registered with this adapter via registerDataSetObserver(DataSetObserver).
android.widget.BaseAdapter的部分源代碼如下: public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter { private final DataSetObservable mDataSetObservable = new DataSetObservable(); public boolean hasStableIds() { return false; } public void registerDataSetObserver(DataSetObserver observer) { mDataSetObservable.registerObserver(observer); } public void unregisterDataSetObserver(DataSetObserver observer) { mDataSetObservable.unregisterObserver(observer); } /** * Notifies the attached View that the underlying data has been changed * and it should refresh itself. */ public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); } public void notifyDataSetInvalidated() { mDataSetObservable.notifyInvalidated(); } -------------------省略------------------ } 註意:對於BaseAdapter,外部可以強行調用notifyDataSetChangednotifyDataSetInvalidated來通知DataSetObserver observer

再分享一下我老師大神的人工智能教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智能的隊伍中來!https://www.cnblogs.com/captainbed

Android數據庫內容變化的監聽