從RecyclerView開始,研究觀察者模式

Android開發(fā)中一個(gè)很常見的場(chǎng)景是有一個(gè)列表,列表上每條Item有個(gè)點(diǎn)贊的按鈕,當(dāng)你給這條Item點(diǎn)贊的時(shí)候,這個(gè)按鈕就會(huì)變成已點(diǎn)贊狀態(tài),相信大家可以腦補(bǔ)到這個(gè)場(chǎng)景。這里就用到了觀察者模式。

那么什么是觀察者模式,我們稍微瞧一下這個(gè)模式的定義,不用太在意,看不懂也沒關(guān)系,我覺得設(shè)計(jì)模式是需要用代碼感受的,加這些定義只是為了讓懂的人感受更深,更系統(tǒng)。
觀察者模式:

觀察者模式

兩個(gè)抽象類或者接口,
一個(gè)是被觀察者的抽象類或者接口,需要持有所有觀察者的引用,
被觀察者要能做三件事兒:
1.可以注冊(cè)觀察者。
2.有注冊(cè)就有注銷。
3.可以通知觀察者的方法。

一個(gè)是觀察者的抽象類或者接口,用來更新自己。
觀察者要能做一件事兒,那就是更新自己。

具體舉個(gè)例子:
雖然我們主要是寫android的,但是服務(wù)器的東西還是要懂點(diǎn),就拿簡(jiǎn)書這個(gè)類似于博客的網(wǎng)站來說吧,如果你訂閱了或者說關(guān)注了一個(gè)領(lǐng)域,就能收到這個(gè)領(lǐng)域文章的推送,如果沒有關(guān)注,則不能。是相當(dāng)于有一個(gè)總控制臺(tái)(被觀察者,持有數(shù)據(jù)源,這里的數(shù)據(jù)源是我們每個(gè)訂閱了的人)通知下面的觀察者。

在java中可以這么寫。(因?yàn)閖ava的觀察者模式是內(nèi)置的,所以你只需要實(shí)現(xiàn)Observer接口和繼承Observable)

觀察者的代碼:

/**
 * 程序員,也就是你,訂閱這個(gè)專題的人是。觀察者
 */

public class Coder implements Observer {
    private String yourName;
    public Coder(String yourName){
        this.yourName=yourName;
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("你訂閱的"+arg.toString()+"更新了。");
    }

    @Override
    public String toString() {
        return "your name "+yourName;
    }
}

以下是被觀察者和服務(wù)器的代碼。

/**
 * 你訂閱的簡(jiǎn)書android領(lǐng)域
 */
public class JianShuAndroid extends Observable{
    public void postNewContentToCoder(String content){
        setChanged();
        notifyObservers(content);
    }
}

/**
 * 服務(wù)器的代碼
 */
public class Server{   
    public static void main(String[] args){
        JianShuAndroid jianShuAndroid=new JianShuAndroid();
        Coder coder1=new Coder("name1");
        Coder coder2=new Coder("name2");
        Coder coder3=new Coder("name3");
        jianShuAndroid.addObserver(coder1);
        jianShuAndroid.addObserver(coder2);
        jianShuAndroid.addObserver(coder3);
        jianShuAndroid.postNewContentToCoder("contentChanged");
    }
}

(這里順便提一下,為什么觀察者是實(shí)現(xiàn)一個(gè)observer接口,而被觀察者是繼承一個(gè)抽象類呢?我覺得,被觀察者寫成抽象類的原因是復(fù)用,觀察者寫成接口的原因是降低代碼的耦合度,面向接口編程,在原則里就是依賴倒置原則,我們倒著思考,如果這里不是接口,而是一個(gè)具體的類,那么,耦合度就相當(dāng)高了,如果不是Coder注冊(cè)就無法添加到observable里,就要修改observable的代碼。)

接下來驗(yàn)證我們看看android的套路是怎么玩的。
文章開頭提到的RecyclerView,整個(gè)列表是一個(gè)被觀察者也就是這個(gè)RecyclerView的Adapter,為什么要叫被觀察者呢?因?yàn)锳dapter持有了所有數(shù)據(jù)源,而每條Item要做的是觀察這些數(shù)據(jù)源有沒有變化,所以就叫被觀察者,每個(gè)Item就叫觀察者。當(dāng)數(shù)據(jù)變化的時(shí)候,adapter就通知這條Item,數(shù)據(jù)源發(fā)生了變化了(這個(gè)通知的過程是notifyDataSetChanged來進(jìn)行的),我們可以猜想,RecyclerView的內(nèi)部的模式是觀察者模式。

玩觀察者模式,假如你用的是java,那么observable,observer是內(nèi)置的,你的被觀察者繼承observable,觀察者實(shí)現(xiàn)observer接口,就行了。其他的語言,如果你搞清了原理,自己寫一個(gè)也很簡(jiǎn)單。

上面我們說到了RecyclerView,開始分析(看源碼,一萬多行的RecyclerView不要急,找我們想要的。我感覺看源碼就是像給了你一團(tuán)毛線讓你理順,當(dāng)然你要去找那個(gè)線頭,千萬不要陷入泥潭,每一行都看。)

大家找一下這個(gè)突破口,也就是線頭,我們明顯現(xiàn)在已經(jīng)知道了recyclerview里肯定用到了觀察者模式,還有一個(gè)重要線索是,我們知道它使怎么通知觀察者改變的,就是notifyDataSetChanged。那就從這個(gè)開始看起。
找了半天,知道了notifyDataSetChanged是在Adapter里面,而這個(gè)adapter開始的第一句就是

public static abstract class Adapter<VH extends ViewHolder> {
        private final AdapterDataObservable mObservable = new AdapterDataObservable();
        private boolean mHasStableIds = false;
  ....
  ....
  ....
}

很熟悉的字眼,observable(被觀察者)來了。看一下這個(gè)AdapterDataObservable,不要怕,名字是長(zhǎng)了點(diǎn),但是一般名字長(zhǎng)的都是紙老虎。

static class AdapterDataObservable extends Observable<AdapterDataObserver> {
        public boolean hasObservers() {
            return !mObservers.isEmpty();
        }

        public void notifyChanged() {
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }

        public void notifyItemRangeChanged(int positionStart, int itemCount) {
            notifyItemRangeChanged(positionStart, itemCount, null);
        }
  ...
  ...
  ...

一看,沒錯(cuò)這家伙肯定是被觀察者,因?yàn)樗^承了Observable。我們那個(gè)簡(jiǎn)書網(wǎng)站就是這樣啊,JianShuAndroid繼承了observable。
還記得我們前面說被觀察者一般要可以做三件事兒。注冊(cè),注銷和通知。那么這里也不會(huì)例外。我們看到的是通知這件事兒,但是注冊(cè)和注銷是java內(nèi)部的Observable幫我們實(shí)現(xiàn)了。

public abstract class Observable<T> {
    /**
     * The list of observers.  An observer can be in the list at most
     * once and will never be null.
     */
    protected final ArrayList<T> mObservers = new ArrayList<T>();

    /**
     * Adds an observer to the list. The observer cannot be null and it must not already
     * be registered.
     * @param observer the observer to register
     * @throws IllegalArgumentException the observer is null
     * @throws IllegalStateException the observer is already registered
     */
    public void registerObserver(T observer) {
        if (observer == null) {
            throw new IllegalArgumentException("The observer is null.");
        }
        synchronized(mObservers) {
            if (mObservers.contains(observer)) {
                throw new IllegalStateException("Observer " + observer + " is already registered.");
            }
            mObservers.add(observer);
        }
    }

    /**
     * Removes a previously registered observer. The observer must not be null and it
     * must already have been registered.
     * @param observer the observer to unregister
     * @throws IllegalArgumentException the observer is null
     * @throws IllegalStateException the observer is not yet registered
     */
    public void unregisterObserver(T observer) {
        if (observer == null) {
            throw new IllegalArgumentException("The observer is null.");
        }
        synchronized(mObservers) {
            int index = mObservers.indexOf(observer);
            if (index == -1) {
                throw new IllegalStateException("Observer " + observer + " was not registered.");
            }
            mObservers.remove(index);
        }
    }
    ....
    ....
    ....

這時(shí)候observable出現(xiàn)了,那observer呢?
其實(shí)上面就已經(jīng)出現(xiàn)了?;厣弦欢未a里找,AdapterDataObserver的泛型里是AdapterDataObserver,然后我們?nèi)炙岩幌戮涂梢园l(fā)現(xiàn)

private class RecyclerViewDataObserver extends AdapterDataObserver {
        RecyclerViewDataObserver() {
        }

        @Override
        public void onChanged() {
            assertNotInLayoutOrScroll(null);
            if (mAdapter.hasStableIds()) {
                // TODO Determine what actually changed.
                // This is more important to implement now since this callback will disable all
                // animations because we cannot rely on positions.
                mState.mStructureChanged = true;
                setDataSetChangedAfterLayout();
            } else {
                mState.mStructureChanged = true;
                setDataSetChangedAfterLayout();
            }
            if (!mAdapterHelper.hasPendingUpdates()) {
                requestLayout();
            }
        }
      ...
      ...
      ...

這就是observer了,這個(gè)是干嘛的,根據(jù)我們上面說的,有一件事兒,那就是更新數(shù)據(jù)的,onChanged里面涉及到requestLayout()的,在requestLayout里會(huì)重新調(diào)用onDraw來重繪。

最后整理一遍這個(gè)過程,
在Adapter里面有一個(gè)AdapterDataObservable,是被觀察者,被觀察者必須有三個(gè)方法,注冊(cè),銷毀,通知,這里的注冊(cè)就是registerAdapterDataObserver,通知就是notify相關(guān)的。
在setAdapter的時(shí)候,將觀察者,也就是RecyclerViewDataObserver注冊(cè)到AdapterDataObservable里面來維護(hù),觀察者里面自然是更新布局。
我們調(diào)用notifyDataSetChanged其實(shí)就是調(diào)用被觀察者的notify相關(guān)方法

關(guān)于觀察者模式,有興趣的話可以看看EventBus和AndroidEventBus的源碼,這兩個(gè)開源庫的實(shí)現(xiàn)也是運(yùn)用了觀察者模式。

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

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,036評(píng)論 25 709
  • 寫在前面 以前聽說過一句話,說是** 自己寫過代碼總量沒有超過10w行談設(shè)計(jì)模式那都是耍流氓 **。我信了,所以一...
    xiasuhuei321閱讀 6,652評(píng)論 20 29
  • 1 場(chǎng)景問題# 1.1 訂閱報(bào)紙的過程## 來考慮實(shí)際生活中訂閱報(bào)紙的過程,這里簡(jiǎn)單總結(jié)了一下,訂閱報(bào)紙的基本流程...
    七寸知架構(gòu)閱讀 4,809評(píng)論 5 57
  • 定義 定義對(duì)象之前一種一對(duì)多的依賴關(guān)系,使得當(dāng)一個(gè)對(duì)象改變狀態(tài),所有依賴這個(gè)對(duì)象的對(duì)象都會(huì)得到通知并且自動(dòng)更新。 ...
    Swy2w閱讀 712評(píng)論 0 2
  • 昨晚跟一個(gè)默契小伙伴(是指這種可以完全不說話都能基本明白對(duì)方心意和心思的小伙伴),以及中午昨天下午跟另外一個(gè)學(xué)霸(...
    路航唐LhT閱讀 216評(píng)論 0 0

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