Cursor和CursorAdapter中的觀察者模式機(jī)制

文章摘要:
1壁熄、觀察者模式構(gòu)建松耦合的架構(gòu)敲董,Cursor和CursorAdapter就是其中一個例子,Cursor空另,CursorAdapter配合ListView一起使用盆耽,當(dāng)數(shù)據(jù)發(fā)生改變的時候,可以實(shí)現(xiàn)列表數(shù)據(jù)自動刷新扼菠,不再需要手動執(zhí)行Cursor.requery摄杂。
2、CursorAdapter與Cursor之間通過觀察者之間建立關(guān)系循榆。Cursor是主題析恢,CursorAdapter是觀察者。
ContentProvider與Cursor之間通過Uri之間建立觀察者模式秧饮,ContentProvider是主題映挂,Cursor是觀察者泽篮。


Cursor,CursorAdapter配合ListView一起使用袖肥,當(dāng)數(shù)據(jù)發(fā)生改變的時候咪辱,可以實(shí)現(xiàn)列表數(shù)據(jù)自動刷新≌窭停現(xiàn)在介紹一下內(nèi)中原理椎组。

工作原理類圖

1、ContentProvider與Cursor之間的關(guān)系历恐。
我們使用Uri向ContentProvider發(fā)起一個query請求用來得到Cursor對象寸癌。但在cursor對象返回之前,我們會給cursor對象執(zhí)行setNotificationUri()方法弱贼。

public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder){  
        SQLiteDatabase db = mOpenHelper.getReadableDatabase();  
          
        Cursor cursor = null;  
        switch(URI_MATCHER.match(uri)){  
        case XXX:  
            break;  
        case XXX:  
            break;  
            ..  
            default:  
                break;  
        }  
        if(cursor != null){  
            cursor.setNotificationUri(getContext().getContentResolver(), XXX.CONTENT_URI);  
        }  
        return cursor;  
    }  

在setNotificationUri方法中執(zhí)行內(nèi)容如下:
類:AbstractCursor.java

public void setNotificationUri(ContentResolver cr, Uri notifyUri) {  
        synchronized (mSelfObserverLock) {  
            mNotifyUri = notifyUri;  
            mContentResolver = cr;  
            if (mSelfObserver != null) {  
                mContentResolver.unregisterContentObserver(mSelfObserver);  
            }  
            mSelfObserver = new SelfContentObserver(this);  
            mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);  
            mSelfObserverRegistered = true;  
        }  
    }  

在這個方法內(nèi)部蒸苇,首先檢查mSelfObserver是否為null,如果不為null,解除mSelfObserver對uri的監(jiān)聽吮旅。然后重新使用new進(jìn)行實(shí)例化一個mSelfObserver對象溪烤,mSelfObserver繼承自ContentObserver。再然后給mSelfObserver注冊Uri(mNotifyUri)監(jiān)聽庇勃。

protected static class SelfContentObserver extends ContentObserver {  
        WeakReference<AbstractCursor> mCursor;  
  
        public SelfContentObserver(AbstractCursor cursor) {  
            super(null);  
            mCursor = new WeakReference<AbstractCursor>(cursor);  
        }  
  
        @Override  
        public boolean deliverSelfNotifications() {  
            return false;  
        }  
  
        @Override  
        public void onChange(boolean selfChange) {  
            AbstractCursor cursor = mCursor.get();  
            if (cursor != null) {  
                cursor.onChange(false);  
            }  
        }  
    }

對以上做一個總結(jié):
在query發(fā)起者得到了一個Cursor對象责嚷,并且這個Cursor對象的實(shí)現(xiàn)類AbstractCursor內(nèi)部會實(shí)例化一個mSelfObserver對象注冊Uri的監(jiān)聽。根據(jù)觀察者模式邏輯罕拂,當(dāng)uri執(zhí)行notify方法時揍异,我們的mSelfObserver會收到通知并且執(zhí)行onChange方法。在這里我們的SelfObserver是觀察者爆班。
2、主題(信息發(fā)布者):通知Cursor中mSelfObserver棍鳖,Uri數(shù)據(jù)發(fā)生改變。
在我們的ContentProvider中的update以及insert或者delete方法中碗旅,我們在方法執(zhí)行的最后按照我們的需求渡处,我們會執(zhí)行如下方法調(diào)用(以update方法為例):

public int update(Uri uri,ContentValues values,String selection,String[] selectionArgs){  
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
        int affectedRows = 0;  
        switch(URI_MATCHER.match(uri)){  
        case XXX:  
            break;  
        case XXX:  
            break;  
            ...  
            default:  
                break;  
        }  
          
        if(affectedRows > 0){  
            getContext().getContentResolver().notifyChange(XXX.CONTENT_URI,null);  
        }  
        return affectedRows;  
    }  

當(dāng)我們執(zhí)行方法

getContext().getContentResolver().notifyChange(XXX.CONTENT_URI,null),

那么AbstractCursor類中的mSelfObserver就會收到通知并且回調(diào)onChange方法医瘫。至于在onChange方法中做了那些工作旧困,我們稍后介紹稼锅,我們先來看一下cursor和cursorAdapter之間的關(guān)系僚纷。
3、Cursor和CursorAdapter之間的關(guān)系怖竭。
當(dāng)我們構(gòu)建CursorAdapter時痊臭,我們會將cursor對象作為CursorAdapter的構(gòu)造參數(shù)傳遞到CursorAdapter中。
類:****CursorAdapter.java

public CursorAdapter(Context context, Cursor c, int flags) {  
        init(context, c, flags);  
}  

void init(Context context, Cursor c, int flags) {  
        if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {  
            flags |= FLAG_REGISTER_CONTENT_OBSERVER;  
            mAutoRequery = true;  
        } else {  
            mAutoRequery = false;  
        }  
        boolean cursorPresent = c != null;  
        mCursor = c;  
        mDataValid = cursorPresent;  
        mContext = context;  
        mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;  
        if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {  
            mChangeObserver = new ChangeObserver();  
            mDataSetObserver = new MyDataSetObserver();  
        } else {  
            mChangeObserver = null;  
            mDataSetObserver = null;  
        }  
  
        if (cursorPresent) {  
            if (mChangeObserver != null)          
                    c.registerContentObserver(mChangeObserver);  
            if (mDataSetObserver != null)   
                    c.registerDataSetObserver(mDataSetObserver);  
        }  
    }  

總結(jié):CursorAdapter通過new關(guān)鍵字初始化了mChangeObserver允趟,mDataSetObserver兩個對象鸦致。并且調(diào)用
c.registerContentObserver(mChangeObserver),c.registerDataSetObserver(mDataSetObserver)抗碰,讓這兩個ContentResolver與Cursor對象建立了聯(lián)系鳍寂。這兩個方法在Cursor中:
類****Cursor.java

public void registerContentObserver(ContentObserver observer) {  
        mContentObservable.registerObserver(observer);  
}  
public void registerDataSetObserver(DataSetObserver observer) {  
        mDataSetObservable.registerObserver(observer);  
}

這里涉及到兩個主題類,mContentObservable與mDataSetObservable都繼承自O(shè)bservable捍壤,主題類有如下幾大功能,將觀察者對象加入到列表鞍爱,收到變化時,遍歷列表通知觀察者盗扇。執(zhí)行觀察者對象的onChange方法沉填。

總結(jié):關(guān)于CursorAdapter與Cursor的關(guān)系,我們可以概括一下斑鼻,Cursor中有一套觀察者模式猎荠,其中維護(hù)了兩個主題蜀备,mContentObservable荒叶,mDataSetObservable。在這套觀察者模式中CursorAdapter提供了兩個觀察者對象mChangeObserver脂凶,mDataSetObserver戈毒。當(dāng)Cursor中的主題通知改變的時候横堡,會觸發(fā)執(zhí)行CursorAdapter中的兩個觀察者中的onChanged方法。

4道宅、CursorAdapter和Cursor以及ContentProvider之間的關(guān)系

CursorAdapter與Cursor之間通過觀察者之間建立關(guān)系胸蛛。Cursor是主題,CursorAdapter是觀察者葬项。
ContentProvider與Cursor之間通過Uri之間建立觀察者模式民珍,ContentProvider是主題,Cursor是觀察者嚷量。

5蝶溶、ContentProvider主題發(fā)布通知,Cursor觀察者的邏輯抖所。

Cursor監(jiān)聽的uri發(fā)生了改變(即Cursor中的mSelfObserver接到通知)田轧,業(yè)務(wù)邏輯。

protected static class SelfContentObserver extends ContentObserver {  
        WeakReference<AbstractCursor> mCursor;  
  
        public SelfContentObserver(AbstractCursor cursor) {  
            super(null);  
            mCursor = new WeakReference<AbstractCursor>(cursor);  
        }  
  
        @Override  
        public boolean deliverSelfNotifications() {  
            return false;  
        }  
  
        @Override  
        public void onChange(boolean selfChange) {  
            AbstractCursor cursor = mCursor.get();  
            if (cursor != null) {  
                cursor.onChange(false);  
            }  
        }  
    }

我們看一下在Cursor.onChange中的業(yè)務(wù)邏輯:
類:AbstractCursor.java

protected void onChange(boolean selfChange) {  
        synchronized (mSelfObserverLock) {  
            mContentObservable.dispatchChange(selfChange);  
            if (mNotifyUri != null && selfChange) {  
                mContentResolver.notifyChange(mNotifyUri, mSelfObserver);  
            }  
        }  
}

執(zhí)行mContentObservable.dispatchChange(false)方法巷查,通過3中可知,執(zhí)行邏輯如下:
類****ContentObservable.java

public void dispatchChange(boolean selfChange) {  
        synchronized(mObservers) {  
            for (ContentObserver observer : mObservers) {  
                if (!selfChange || observer.deliverSelfNotifications()) {  
                    observer.dispatchChange(selfChange);  
                }  
            }  
 }

總結(jié):CursorProvider主題發(fā)送通知旭寿,身為觀察者的Cursor收到變化并執(zhí)行自身的onChange方法崇败。但Cursor也是主題,其收到變化缩膝,會通知其所有的觀察者對象岸霹,也就是CursorAdapter。
6贡避、CursorAdapter中觀察者的邏輯。

類****CursorAdapter$ChangeObserver.java

private class ChangeObserver extends ContentObserver {  
        public ChangeObserver() {  
            super(new Handler());  
        }  
  
        @Override  
        public boolean deliverSelfNotifications() {  
            return true;  
        }  
  
        @Override  
        public void onChange(boolean selfChange) {  
            onContentChanged();  
        }  
}

onContentChanged方法:

protected void onContentChanged() {  
        if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {  
            mDataValid = mCursor.requery();  
        }  
}   

在這里我們找到了我們的答案湖饱,mCursor.requery(),會重新刷新并填充mCursor對象井厌。
然后還沒有結(jié)束:
我們的cursor重新填充了致讥,但是不會告訴Adapter執(zhí)行notifyDataSetChanged()方法,因?yàn)橹挥袌?zhí)行了這個方法蝇恶,我們的界面才會刷新惶桐。
7、通知Adapter執(zhí)行notifyDataSetChanged()方法姚糊。
當(dāng)我們的Cursor執(zhí)行requery方法的時候,我們看一下業(yè)務(wù)邏輯:
類:****AbstractCursor.java

public boolean requery() {  
        if (mSelfObserver != null && mSelfObserverRegistered == false) {  
            mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);  
            mSelfObserverRegistered = true;  
        }  
        mDataSetObservable.notifyChanged();  
        return true;  
}  

我們看到我們最后一位主角登場了贸辈,他就是mDataSetObservable肠槽,通過3、可知嘴拢,這是個主題,當(dāng)它notifyChanged的時候赌结,它的所有的觀察者會執(zhí)行onChanged方法孝冒。
我們看一下觀察者的業(yè)務(wù)邏輯:
類:****CursorAdapter$MyDataSetObserver.java

private class MyDataSetObserver extends DataSetObserver {  
        @Override  
        public void onChanged() {  
            mDataValid = true;  
            notifyDataSetChanged();  
        }  
  
        @Override  
        public void onInvalidated() {  
            mDataValid = false;  
            notifyDataSetInvalidated();  
        }  
} 

在onChanged方法中,我們看到了我們的答案量承,notifySetChanged()啼染;

總結(jié):我們在使用Cursor焕梅,CursorAdapter搭配ListView進(jìn)行上層業(yè)務(wù)開發(fā)的過程中,我們只要合理的利用android提供的框架斜棚。我們可以在查詢數(shù)據(jù)庫之后该窗,自動進(jìn)行頁面的刷新。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末义钉,一起剝皮案震驚了整個濱河市规肴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌删壮,老刑警劉巖兑牡,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件均函,死亡現(xiàn)場離奇詭異菱涤,居然都是意外死亡洛勉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門翻擒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來牛哺,“玉大人,你說我怎么就攤上這事巩趁〈靖剑” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵别凹,是天一觀的道長洽糟。 經(jīng)常有香客問我,道長坤溃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任祠饺,我火速辦了婚禮汁政,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘试疙。我一直安慰自己抠蚣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布怀跛。 她就那樣靜靜地躺著,像睡著了一般忠蝗。 火紅的嫁衣襯著肌膚如雪漓拾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天速种,我揣著相機(jī)與錄音低千,去河邊找鬼。 笑死棋傍,一個胖子當(dāng)著我的面吹牛难审,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拂铡,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼葱绒,長吁一口氣:“原來是場噩夢啊……” “哼斗锭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起帮毁,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤烈疚,失蹤者是張志新(化名)和其女友劉穎聪轿,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡金赦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年夹抗,在試婚紗的時候發(fā)現(xiàn)自己被綠了纵竖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡沽甥,死狀恐怖乏奥,靈堂內(nèi)的尸體忽然破棺而出邓了,到底是詐尸還是另有隱情恨诱,我是刑警寧澤骗炉,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布句葵,位于F島的核電站,受9級特大地震影響剂碴,放射性物質(zhì)發(fā)生泄漏轻专。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一催训、第九天 我趴在偏房一處隱蔽的房頂上張望宗收。 院中可真熱鬧,春花似錦混稽、人聲如沸审胚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至临谱,卻和暖如春奴璃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苟穆。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工雳旅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人攒盈。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓拂玻,卻偏偏與公主長得像迎变,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子喉酌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容