ContentProvider和Cursor以及CursorAdapter三者之間內部鏈接實現原理 解析

最近 在學習Android3.0中推出的 Loader 機制,其中CursorLoader 這個加載器說是可以實時監測數據和更新數據,爲了一探究竟,就連帶的將 ContentProvider和Cursor以及CursorAdapter三者間的內部交互分析了下,然而本章內容主要就是將這一塊,至於Loader機制準備,下一篇來具體分析。

對於這三個類我們知道,Contentprovider就是一個Android中進程間的內容共享機制,我們可以使用ContentResolver這個工具嫁接 目標 URI 來訪問對應的Contentprovider,從而獲取目標Cursor數據,Android中 使用Sqliet就是這樣一個機制。然而在這三個類之間其實存在了兩處的觀測者模式的運用。第一處在於Cursor 和 Contentprovider 之間,第二處在於 Cursor 和 CursorAdapter 之間,下面我們先來看一張時序圖大致的瞭解下。Ps: 時序圖 有哪裏不對的還請及時指出啊。
這裏寫圖片描述

上面說到觀察者模式的運用 第一處在於Cursor 和 Contentprovider 之間,我們可以通過上面的時序圖來加以分析,當我們通過 ContentResolver 對目標ContentProvider的數據進行CRUD(增刪改查)操作時,在返回目標Cursor數據之前,我們發現在每個CRUD操作中有一個setNotifycationUri()這個方法,那麼這個方法裏到底做了什麼呢,我們可以看看。

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;  
        }  
    }  
複製代碼

我們可以發現,這裏它創建了一個SelfContentObserver的對象並且給它註冊了Uri監聽。這裏SelfContentObserver看起源碼知道了它繼承了ContentObserver,就是一個Observer,這樣一來當Uri變動時,我們就可以通知它了。注意了在我們進行CRUD操作時,我們經常會加一句 :getContext().getContentResolver().notifyChange(XXX.CONTENT_URI,null),那麼這樣一來Cursor類中的mSelfObserver就會收到通知並且回調onChange方法,到這裏我們是不是可以看出來了這就是觀察者模式的運用呢。

至於第二處則在於 Cursor 和 CursorAdapter 之間,同樣的 我們也可從上面的時序圖中發現。CursorAdapter中持有兩個觀察者:mChangeObserver和mDataSetObserver.這兩個Observer在 CursorAdapter初始化時或者調用其changeCursor(Cursor c)或swapCursor(Cursor c)方法時,就被註冊到Cursor中了,三種方式的代碼依次如下:

void init(Context context, Cursor c, int flags) {
       ...省略
        mCursor = c;
         ...省略
        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);
        }
    }


    void init(Context context, Cursor c, int flags) {
       ...省略
        mCursor = c;
         ...省略
        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);
        }
    }
public Cursor swapCursor(Cursor newCursor) {
        if (newCursor == mCursor) {
            return null;
        }
        Cursor oldCursor = mCursor;
        if (oldCursor != null) {
            if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
            if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
        }
        mCursor = newCursor;
        if (newCursor != null) {
            if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
            if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
            mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
            mDataValid = true;
            // notify the observers about the new cursor
            notifyDataSetChanged();
        } else {
            mRowIDColumn = -1;
            mDataValid = false;
            // notify the observers about the lack of a data set
            notifyDataSetInvalidated();
        }
        return oldCursor;
    }

我們可以看到,這兩個Observer在初始化Adapter的時候被創建,而後會在不同情況下注冊到Cursor中。這裏是因爲Cursor中持有兩個目標對象:mContentObservable和mDataSetObservable 這兩個類就繼承了Observable接口。所以其實是它們兩分別接受了Observer的註冊。代碼如下:

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

到這裏第二處觀察者模式運用就顯示出來啦!

從時序圖中,我們可以看到當Cursor類中的mSelfObserver收到通知後就會調用onChange方法
protected void onChange(boolean selfChange) {  
        synchronized (mSelfObserverLock) {  
            mContentObservable.dispatchChange(selfChange);  
            if (mNotifyUri != null && selfChange) {  
                mContentResolver.notifyChange(mNotifyUri, mSelfObserver);  
            }  
        }  
}

我們可以看到 它會觸發mContentObservable這個目標對象去調用dispatchChange()方法

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

到這裏,它接着就通知其註冊的各個Observer去執行dispatchChange()方法,前面我們已經知道了mContentObservable了被註冊了ChangeObserver 的實例 mChangeObserver,這裏呢首先會執行ChangeObserver的父類ContentObserver的dispatchChange(false)方法:

public final void dispatchChange(boolean selfChange) {  
        if (mHandler == null) {  
            onChange(selfChange);  
        } else {  
            mHandler.post(new NotificationRunnable(selfChange));  
        }  
}  

接着就來到其子類實例mChangeObserver的dispatchChange()方法:

private class ChangeObserver extends ContentObserver {  
        public ChangeObserver() {  
            super(new Handler());  
        }  

        @Override  
        public boolean deliverSelfNotifications() {  
            return true;  
        }  

        @Override  
        public void onChange(boolean selfChange) {  
            onContentChanged();  
        }  
}  

在其Onchange()方法中調用了onContentChanged()方法:

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

到這裏 我們是不是恍然大悟了,mCursor.requery()則就會重新刷新並填充mCursor對象。然後還沒有結束:我們的cursor重新填充了,但是不會告訴Adapter執行notifyDataSetChanged()方法,因爲只有執行了這個方法,我們的界面纔會刷新。

所以我們接着看下mCursor.requery()的內部做了些什麼:

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

我們可以看到,mDataSetObservable.notifyChanged();這個就會 會觸發mDataSetObservable去通知其內部註冊的observer,前面我們也講了mDataSetObservable被註冊了 CursorAdapter中的 MyDataSetObserver的實例 mDataSetObserver,所以我們接着看下mDataSetObserver的onchange()方法的實現:

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

        @Override  
        public void onInvalidated() {  
            mDataValid = false;  
            notifyDataSetInvalidated();  
        }  
}  

在該方法中調用了 notifyDataSetChanged(); 這個方法幹嘛了呢,我們不僅要問,是不是它就是用來刷新界面呢?這個方法用的是子父類Baseadapter的,

BaseAdapetr

public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

我們可以看到,在其源碼中,它會調用其父類BaseAdapetr中mDataSetObservable去通知其中被註冊的Observer,那這個observer到底在哪裏被註冊的呢,這裏呢 也就不饒彎子了一步到位,回到我們使用CursorAdapter的最初,但我們初始化完成它的時候,我們是不是接着會調用setAdapter()方法,將該Adapter設置到目標列表中,那麼這裏又做了什麼呢?

public void setAdapter(ListAdapter adapter) {

       ...省略

        if (mAdapter != null) {

       ...省略

            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);

             ...省略


        requestLayout();
    }

在這裏我們找到了我們的答案,原來這個被註冊的observer就是AdapterDataSetObserver,那這下就好啦,我們轉到其內部的onchange()去一探究竟:

public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            mItemCount = getAdapter().getCount();

            // Detect the case where a cursor that was previously invalidated has
            // been repopulated with new data.
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            requestLayout();
        }

果不其然,原來它通過 requestLayout();來完成接下來的操作了去刷新界面,其內部就是Android中View的繪製機制了,感興趣的話可以去了解哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章