Android學習 ContentProvider數據更新與Observer模式
一 Observer模式
意圖:
定義對象之間一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被更新。
依賴(Dependents)、發布-訂閱(Publish-Subscribe)。處理一對多情況下對象之間的依賴關系。
對象之間必然會存在依賴關系或者依賴關系會處於變動之中,如何解決依賴關系使他們之間的耦合性達到最小。
適用性:
l 當一個抽象模型有兩個方面,其中一個方面依賴於另一個方面。將二者封裝在獨立的對象以使他們各自獨立的改變和復用;
l 當一個對象的改變需要同時改變其他對象,而不知道具體有多少對象有待改變。
l 當一個對象必須通知其他對象,而它又不能假定其他對象是誰。
結構:
理解:
Subject維護對象的狀態,當狀態改變需要立即通知Observer更新狀態與Subject保持狀態的一致;
使對象狀態維護者Subject並不需要去關心有哪些Observer關注或者依賴於所維護的對象,而專心負責維護對象;
同時如果某客戶需要對某Subject所維護的對象感興趣,可以通過對此Subject注冊一個Observer來監聽此對象的變化;
不需要監聽的時候也可以注銷掉監聽;實現動態的關注對象狀態的變化。
Subject維護的對象狀態誰會去改變呢?書上提到是某Observer去改變,但也不一定如此;
Subject維護某一對象,對象的具體如何改變可以和Subject是沒有關系,僅僅是知道對象狀態改變了,
需要通知其他對象;對象狀態的改變是有很多層次或者方式進行,不限於由Observer來改變。
更新由誰來觸發呢?一是可以誰改變對象的狀態誰來觸發更新,這樣各Observer狀態的可以得到立即更新,
而且不需要客戶端去負責更新,屬於自動進行更新,但是每一次的對象狀態變化都會執行更新,然而往往對象狀態變化
是多個連續性的變化或者好幾個變化間隔非常短,造成更新次數過多又非必要性的,降低效率;
二是由客戶端負責更新,就可以控制何時進行更新最合適,避免整個狀態變化中進行更新,而是整個連續性狀態改變完成之后
一次性更新,但是此舉將更新責任交給客戶端,往往造成遺漏等不確定的缺陷。
對象狀態變化怎么傳遞?一是在執行更新Update時,將對象以參數的形式傳遞給Observer一步到位——推模型,
對象的類型如何?(Subject\State\…),提供太多數據和細節可能造成某些缺陷,而且提供這些數據並不總是需要;
二是提供接口供Observer訪問獲取狀態或者變化細節——拉模型,這樣造成雙向的通信形式,且不能確定提供什么樣信息合適,
依賴性太強耦合性高;三是通過其它方式渠道,主動的去查詢獲取對象的狀態;所以這三種方式都有其優缺點,
對象狀態變化會有多種情況,如何傳遞狀態變化的信息,可能需要具體問題具體分析解決。
對象變化了,會觸發Subject的更新通知函數執行,完全不用關心誰關注此變化,有多少對象關注此變化;
而且都是通過抽象類實現,可以完全針對此接口進行編程,依賴於抽象而不是實現,降低Subject和Observer之間的耦合性。
所以Subject可以不知道有何Observer和多少Observer的存在,Observer需要知道Subject的存在。
二 Android中信息列表數據更新流程
在信息列表信息數據的變化需要及時反饋到界面上來,數據的存儲是以SQLite數據庫存儲,
以ContentProvider形式訪問;數據變化時是如何更新的呢?
看一下面這個圖:
看到信息部分數據變化更新實現是在CursorAdapter中;
——>ContentObserver監聽到數據變化消息之后;
——>通知CursorAdapter數據內容有變化;
——>通知信息相關的類進行數據更新重新進行查詢;
ContentObserver從名稱看得出來這是一個Observer模式的應用;
那么ContentObserver是如何實現監聽有數據變化的呢?
ContentObserver在CursorAdapter中,肯定是CursorAdapter有關系,需要搞清楚ContentObserver與CursorAdapter之間的關系。
下面學習一下CursorAdapter中ContentObserver監聽數據變化的流程;
三 Android中CursorAdapter的ContentObserver監聽數據變化流程
要搞清楚ContentObserver與CursorAdapter之間的關系,以及CursorAdapter與AbstractCursor之間更深層次之間的關系,
才能弄清楚誰是Subject,如何注冊的Observer,在數據變化時,Subject是如何通知到Observer的。
下面將采用倒推的方式一步一步的學習:
1 CursorAdapter中ContentObserver的注冊到AbstractCursor過程
ContentObserver(抽象類)就是用來接收數據變化時的觀察者,能進行異步派發派發接收到變化的通知。
public abstract class ContentObserver { private Transport mTransport; Handler mHandler; public ContentObserver(Handler handler) {} public IContentObserver getContentObserver() {}
//需要重寫onChange public void onChange(boolean selfChange) {} public final void dispatchChange(boolean selfChange) {} }
在CursorAdapter初始化時:
void init(Context context, Cursor c, boolean autoRequery) { // ChangeObserver 繼承 ContentObserver
mChangeObserver = new ChangeObserver(); //交給Cursor注冊Observer
c.registerContentObserver(mChangeObserver); }
Cursor中注冊Observer過程:
Cursor是一個接口真正干活的是它實現者AbstractCursor
看下AbstractCursor 的registerContentObserver: void registerContentObserver(ContentObserver observer) { mContentObservable.registerObserver(observer); }
ContentObservable又是什么呢?以及ContentObserver是怎么樣一個類呢?
ContentObservable和前面ContentObserver不同:
ContentObservable類:
public class ContentObservable extends Observable<ContentObserver> { public void registerObserver(ContentObserver observer) {} public void dispatchChange(boolean selfChange) {} public void notifyChange(boolean selfChange) {} } public abstract class Observable<T> { protected final ArrayList<T> mObservers = new ArrayList<T>(); public void registerObserver(T observer) {} public void unregisterObserver(T observer) {} public void unregisterAll() { }
所以ContentObservable就是專門用來注冊ContentObserver,負責管理AbstractCursor作為Subject時接收注冊Observer的,
而AbstractCursor此處就是Subject;
Observer——》ChangeObserver(CursorAdapter內部類)
Subject ——》AbstractCursor(成員ContentObservable負責管理Observer)
觸發更新Update:
既然AbstractCursor作為此處的Subject,那么觸發Observer更新是在何時進行呢?
AbstractCursor有這樣一個接口:
protected void onChange(boolean selfChange) { //觸發所有的Observer mContentObservable.dispatchChange(selfChange); //這又是何緣故呢沒搞懂 這里不會執行 下面再分析
if (mNotifyUri != null && selfChange) { mContentResolver.notifyChange(mNotifyUri, mSelfObserver); } }
ContentObservable類中:
public void dispatchChange(boolean selfChange) { for (ContentObserver observer : mObservers) { if (!selfChange || observer.deliverSelfNotifications()) { observer.dispatchChange(selfChange); } } }
ContentObserver類中:
public final void dispatchChange(boolean selfChange) { if (mHandler == null) { onChange(selfChange); } else {
//異步的派發變化時通知 mHandler.post(new NotificationRunnable(selfChange)); } }
這樣就觸發了所有注冊到AbstarctCursor中ContentObserverable負責管理的所有ContentObserver;
此處便是CursorAdapter中的內部類ChangeObserver
下面看一下這個層次上的結構類圖:
觸發更新CursorAdapter中ChangeObserver中onChange函數執行;
這樣就到了CursorAdapter更新流程上來了,這里屬於Framework層;
從CursorAdapter然后就到了Application層,如前面所述的信息列表的更新流程。
這里的Observer模式中:
Subject——>AbstractCursor
Observer——>ChangeObserver(CursorAdapter內部類)
那么到此AbstractCursor是如何改變其中對象的狀態和何時使其執行通知所有的觀察者的呢?
作為Curosr雖然接口中提供了Update等操作來改變數據,但其實在AbstractCursor等並沒有提供支持
來使用Update操作來改變數據,可以看到在4.0代碼中已經將Update操作去掉;
Cursor僅僅是作為database query返回的結果用於獲取其中的數據,所以AbstractCursor及其子類維護
其中的某個數據對象,可以自己實現對象狀態的改變維護如MatrixCursor,也可以僅僅負責某個數據對象的維護,
數據對象狀態真正改變不用去負責,僅需要在數據有變化時得到通知即可如SQLiteCursor;
所以還需要弄清楚Curosr作為Subject是維護的對象狀態是如何變化且被通知到的。
下面看一下AbstractCursor作為Subject其中的對象狀態改變是如何進行和被通知到的。
四 Android中AbstractCursor對象狀態變化通知
更新通知啟動:
從上面代碼分析中可以知道AbstractCursor得到內容變化並通知其Observers是在下面函數中進行的:
protected void onChange(boolean selfChange) { mContentObservable.dispatchChange(selfChange); if (mNotifyUri != null && selfChange) { mContentResolver.notifyChange(mNotifyUri, mSelfObserver); } }
順着這條線找到onChange是何時被調用的:
找到位置是在AbstractCursor內部類SelfContentObserver中有如下:
protected static class SelfContentObserver extends ContentObserver { WeakReference<AbstractCursor> mCursor; public void onChange(boolean selfChange) { AbstractCursor cursor = mCursor.get(); //這里調用其依賴類的上述的onChange類中
cursor.onChange(false); //這里傳遞的false
} }
這又是一個ContentObserver觀察者類,這又是怎么回事呢?Go On……
SelfContentObserver作為觀察者:
找到SelfContentObserver使用方式:AbstractCursor中
public void setNotificationUri(ContentResolver cr, Uri notifyUri) { mNotifyUri = notifyUri; mContentResolver = cr; mSelfObserver = new SelfContentObserver(this); //進行Observer的注冊
mContentResolver.registerContentObserver( mNotifyUri, true, mSelfObserver); }
SelfContentObserver作為了ContentResolver的一個Observer;
那函數setNotificationUri何時被調用呢?
答案是:ContentProvider中的Query()函數注釋中有描述,派生類中需要調用這個c.setNotificationUri。
那么現在注冊任務交給了ContentResolver了。
ContentService登場:
ContentResolver中注冊Observer時
public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer) { getContentService().registerContentObserver(uri, notifyForDescendents, observer.getContentObserver()); }
看到注冊Observer使用的是ContentService進行的,這是個系統服務SystemService;
ContentService又是干什么的勾當的呢?
從名稱看到是內容服務,主要是數據庫等提供解決方法的服務。因為數據庫SQlite是一個C庫,其中東東涉及很多需要。
ContentService中注冊Observer函數:
public void registerContentObserver(Uri uri, boolean notifyForDescendents, IContentObserver observer) { mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode, Binder.getCallingUid(), Binder.getCallingPid()); }
看到此函數有三個參數分別代表什么意義呢:
uri:針對對有變化的感興趣進行監聽的URI
notifyForDescendents:true表示以uri前綴開始的任何變化都進行通知;false表示完全匹配才進行通知;
observer:IContentObserver接口,提供了一個方法onChange,變化發生時Cursor需要更新時調用
使用此ContentService的registerContentObserver接口注冊Observer,
通過指定Uri可以僅對數據庫中感興趣的數據有變化時,進行監聽。具體實現細節可以看下ContentService源代碼
中是如何構建一個樹形結構來管理觀察者對感興趣數據的監聽,看到根節點就是上述的mRootNode構建了一顆大樹。
注冊之后就是等待數據有變化時,進行監聽了;此一布又是如何進行的呢?
觸發數據更新通知:
對數據庫中數據的更改操作都是通過ContentResolver 中使用ContentProvider進行修改的,數據變化就來源於此;
看看ContentProvider中數據修改函數Insert中都干了些什么。
ContentProvider是個abstract類,其中的數據更改操作的函數都是純虛函數,但是看一下其中的Insert函數的注釋:
As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver)
notifyChange()}after inserting.
在使用此函數插入數據之后,需要調用類ContentResolver中函數notifyChange,所以其子類中需要做這個事情;
可以看到在子類Insert函數中都執行了:
getContext().getContentResolver().notifyChange(newUri, null)
通過此到了ContentResolver的nofifyChange中,Go On……
ContentResolver中函數:
void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { getContentService().notifyChange(……); }
進入到ContentService中的數據變化通知更新策略中;注冊Observer是在ContentService中,
數據變化通知Observer更新數據也必然是要在ContentService中。下面看看這是如何執行的:
ContentService通知Observer更新:
ContentService函數中的notifyChange函數較為復雜,因為我們是注冊對感興趣的數據變化時才需要被通知到,
所以此處通過對樹形結構存儲的Observer,進行遍歷查找到對變化感興趣的Observer。
public void notifyChange(Uri uri, IContentObserver observer, boolean observerWantsSelfNotifications, boolean syncToNetwork)
{ //用於保存對此變化感興趣的Observer
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); //收集對此變化感興趣的Observer
mRootNode.collectObserversLocked(...,calls); //發布數據變化通知到相應的Observer
final int numCalls = calls.size(); for (int i=0; i<numCalls; i++) { ObserverCall oc = calls.get(i); oc.mObserver.onChange(oc.mSelfNotify); } }
此處就走到了哪里呢?必然正是我們前面在AbstractCursor中所注冊的SelfContentObserver的onChange函數中,
然后就到了AbstractCursor作為Subject時這一層的Observer模式的通知機制中。
這又是一個基於Observer模式的Subject——》Observer結構。
Subject:ContentService;
Observer:SelfContentObserver(AbstractCursor中內部類)
上面所述數據改變時從ConentService通知其關注這一變化的Observer;這一過程的類結構圖大致如下:
五 Android中ContentService數據變化通知更新流程
從上面的整個代碼流程可以看到這個過程中使用兩個層次的Observer模式:
下面是注冊Observer的時候的大致流程:
下面是數據變化時通知更新流程圖:
整個過程大致如上所述,有兩個層次的Observer模式的應用。從中我們可以看到Subject可以不同,
Observer相同,使Observer可以被復用,Subject不用去關心Observer,
Observer當然是要知道Subject存在的,Observer且能動態的添加刪除。
具體ContentProvider更新過程可以參考這篇文章:
Android應用程序組件Content Provider的共享數據更新通知機制分析
地址:http://blog.csdn.net/Luoshengyang/article/details/6985171