ContentObserver是android上監(jiān)聽內(nèi)容變化的常用工具,既可以監(jiān)聽本應用的,也可以監(jiān)聽其他應用的內(nèi)容變化。通常有兩種用法:
- ContentResolver.registerContentObserver(Uri uri, ContentObserver observer):
用于監(jiān)聽指定uri的內(nèi)容變化 - Cursor.registerContentObserver(ContentObserver observer):
用于監(jiān)聽指定cursor指向的內(nèi)容變化
那么監(jiān)聽器是如何注冊的?通知又是如何發(fā)出來的呢?先看下通過ContentResovler注冊的方式
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
ContentObserver observer, int userHandle) {
try {
getContentService().registerContentObserver(uri, notifyForDescendents,
observer.getContentObserver(), userHandle);
} catch (RemoteException e) {
}
}
可以看到, 這里首先通過getContentService()獲取獲取了一個ContentService的系統(tǒng)服務,然后獲取了observer的一個binder對象,并注冊給ContentService。
再看下getContentSerivce的是實現(xiàn):
/** @hide */
public static IContentService getContentService() {
if (sContentService != null) {
return sContentService;
}
IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
if (false) Log.v("ContentService", "default service binder = " + b);
sContentService = IContentService.Stub.asInterface(b);
if (false) Log.v("ContentService", "default service = " + sContentService);
return sContentService;
}
可以看到ContentService確實是注冊在ServiceManager中的一個系統(tǒng)服務。到這里就可以確認ContentResolver注冊ContentPrvider是將一個Binder的回調(diào)對象注冊到了SystemServer的ContentService中了,所以不需要被監(jiān)聽的內(nèi)容必須在運行。
那么通知是如何實現(xiàn)的呢?
通過ContentReslover的notifyChange(Uri uri)實現(xiàn),
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
int userHandle) {
try {
getContentService().notifyChange(
uri, observer == null ? null : observer.getContentObserver(),
observer != null && observer.deliverSelfNotifications(), syncToNetwork,
userHandle);
} catch (RemoteException e) {
}
}
也就是說注冊和通知其實都是通過ContentService解耦了,通知方不用有多少人注冊了,只需要向ContentService發(fā)送一個內(nèi)容改變的通知即可,通過uri告訴ContentService那部分內(nèi)容發(fā)生來改變,ContentService再向所有的注冊該uri ContentObserver的進程發(fā)送通知。
接下來再看看Cursor是怎么注冊的?
一般而言,Cursor是通過ContentProvider查詢返回的,這就涉及到兩種情況:
- 跨進程查詢返回到Cursor
- 本進程查詢返回的Cursor
對于跨進程查詢返回的Cursor,在ContentProviderNative的onTransact方法中就已經(jīng)注冊了一個caller的ContentObserver調(diào)用,后續(xù)的注冊只需要在caller進程增加本地的ContentObserver對象即可
對于本進程查詢,直接注冊本地ContentObserver即可.
但是!Cursor注冊了ContentObserver之后,只能在Cursor變化的時候通知監(jiān)聽者,而Cursor本身并不能監(jiān)聽內(nèi)容變化,所有要實現(xiàn)監(jiān)聽,必須調(diào)用setNotificationUri(ContentResolver resolver, Uri uri)
AbstractCursor.java
public void setNotificationUri(ContentResolver cr, Uri notifyUri, int userHandle) {
synchronized (mSelfObserverLock) {
mNotifyUri = notifyUri;
mContentResolver = cr;
if (mSelfObserver != null) {
mContentResolver.unregisterContentObserver(mSelfObserver);
}
mSelfObserver = new SelfContentObserver(this);
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver, userHandle);
mSelfObserverRegistered = true;
}
}
所以,Cursor的注冊其實也是通過ContentResolver完成
綜上: 無論通過ContentResolver還是Cursor注冊ContentObserver,其注冊與通知都是通過SystemServer的ContentService完成,即使要監(jiān)聽的內(nèi)容都在本進程中,也會通過binder調(diào)用完成通知。