ContentObserver原理

ContentObserver是android上監(jiān)聽內(nèi)容變化的常用工具,既可以監(jiān)聽本應用的,也可以監(jiān)聽其他應用的內(nèi)容變化。通常有兩種用法:

  1. ContentResolver.registerContentObserver(Uri uri, ContentObserver observer):
    用于監(jiān)聽指定uri的內(nèi)容變化
  2. 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查詢返回的,這就涉及到兩種情況:

  1. 跨進程查詢返回到Cursor
  2. 本進程查詢返回的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)用完成通知。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容