Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android資訊 >> Android設計模式源碼解析之ListView觀察者模式

Android設計模式源碼解析之ListView觀察者模式

編輯:Android資訊

1. 模式介紹

模式的定義

定義對象間一種一對多的依賴關系,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新。

模式的使用場景

  • 關聯行為場景。需要注意的是,關聯行為是可拆分的,而不是“組合”關系;
  • 事件多級觸發場景;
  • 跨系統的消息交換場景,如消息隊列、事件總線的處理機制。

2. UML類圖

uml

角色介紹

  • 抽象主題 (Subject) 角色
    抽象主題角色把所有觀察者對象的引用保存在一個聚集(比如ArrayList對象)裡,每個主題都可以有任意數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象,抽象主題角色又叫做抽象被觀察者(Observable)角色。
  • 具體主題 (ConcreteSubject) 角色
    將有關狀態存入具體觀察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者(Concrete Observable)角色。
  • 抽象觀察者 (Observer) 角色
    為所有的具體觀察者定義一個接口,在得到主題的通知時更新自己,這個接口叫做更新接口。
  • 具體觀察者 (ConcreteObserver) 角色
    存儲與主題的狀態自恰的狀態。具體觀察者角色實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態 像協調。如果需要,具體觀察者角色可以保持一個指向具體主題對象的引用。

3. 模式的簡單實現

簡單實現的介紹

AndroidWeekly是一個每周都會發布關於Android新技術、開源庫、招聘信息等內容的網站,在這裡我們可以看到最新的技術,最牛X的工程師,經常逛逛這類網站不僅能夠開闊我們的眼界,也能讓我們接觸到最前言的科技信息。這其實就是一個RSS系統,用戶訂閱Android Weekly的文章,每當有更新的時候將新的內容推送給訂閱用戶。這不就是觀察者模式嗎?觀察者模式的另一個名字叫做發布-訂閱模式,下圖就是我訂閱AndroidWeekly之後他們發來的確認郵件。下面讓我們來簡單模擬一下AndroidWeekly的發布過程吧!

android-weekly

實現源碼

/**
 * 程序員是觀察者
 * 
 * @author mrsimple
 */
public class Coder implements Observer {
    public String name ;

    public Coder(String aName) {
        name = aName ;
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println( "Hi, " +  name + ", AndroidWeekly更新啦, 內容 : " + arg);
    }

    @Override
    public String toString() {
        return "碼農 : " + name;
    }

}

/**
 * AndroidWeekly這個網站是被觀察者,它有更新所有的觀察者 (這裡是程序員) 都會接到相應的通知.
 * 
 * @author mrsimple
 */
public class AndroidWeekly extends Observable {

    public void postNewPublication(String content) {
        // 標識狀態或者內容發生改變
        setChanged();
        // 通知所有觀察者
        notifyObservers(content);
    }
}

// 測試代碼
public class Test {
    public static void main(String[] args) {
        // 被觀察的角色
        AndroidWeekly androidWeekly = new AndroidWeekly();
        // 觀察者
        Coder mrsimple = new Coder("mr.simple");
        Coder coder1 = new Coder("coder-1");
        Coder coder2 = new Coder("coder-2");
        Coder coder3 = new Coder("coder-3");

        // 將觀察者注冊到可觀察對象的觀察者列表中
        androidWeekly.addObserver(mrsimple);
        androidWeekly.addObserver(coder1);
        androidWeekly.addObserver(coder2);
        androidWeekly.addObserver(coder3);

        // 發布消息
        androidWeekly.postNewPublication("新的一期AndroidWeekly來啦!");
    }

}

輸入結果:

Hi, coder-3, AndroidWeekly更新啦, 內容 : 新的一期AndroidWeekly來啦!
Hi, coder-2, AndroidWeekly更新啦, 內容 : 新的一期AndroidWeekly來啦!
Hi, coder-1, AndroidWeekly更新啦, 內容 : 新的一期AndroidWeekly來啦!
Hi, mr.simple, AndroidWeekly更新啦, 內容 : 新的一期AndroidWeekly來啦!

可以看到所有訂閱了AndroidWeekly的用戶都受到了更新消息,一對多的訂閱-發布系統這麼簡單就完成了。

這裡Observer是抽象的觀察者角色,Coder扮演的是具體觀察者的角色;Observable對應的是抽象主題角色,AndroidWeekly則是具體的主題角色。Coder是具體的觀察者,他們訂閱了AndroidWeekly這個具體的可觀察對象,當AndroidWeekly有更新時,會遍歷所有觀察者 ( 這裡是Coder碼農 ),然後給這些觀察者發布一個更新的消息,即調用Coder中的update方法,這樣就達到了1對多的通知功能。Observer和Observable都已經內置在jdk中,可見觀察者模式在java中的重要性。

Android源碼中的模式實現

ListView是Android中最重要的控件,沒有之一。而ListView最重要的一個點就是Adapter,在Android設計模式源碼解析之適配器(Adapter)模式中我們分析了Adapter模式,在我們往ListView添加數據後,我們都會調用一個方法: notifyDataSetChanged(), 這是為什麼呢? 今天我們就來揭開它的神秘面紗。

第一步我們就跟進這個方法notifyDataSetChanged方法,這個方法定義在BaseAdapter中,代碼如下:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    // 數據集觀察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    // 代碼省略

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

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }

    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     * 當數據集用變化時通知所有觀察者
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
}

我們一看BaseAdapter上述代碼,大體有了這麼一個了解,原來BaseAdapter是一個觀察者模式!
那麼BaseAdapter是如何運作的? 這些觀察者又是什麼呢?我們一步一步來分析。

我們先跟到mDataSetObservable.notifyChanged()函數中看看。

/**
 * A specialization of Observable for DataSetObserver that provides methods for
 * invoking the various callback methods of DataSetObserver.
 */
public class DataSetObservable extends Observable<DataSetObserver> {
    /**
     * Invokes onChanged on each observer. Called when the data set being observed has
     * changed, and which when read contains the new state of the data.
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // 調用所有觀察者的onChanged方式
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
    // 代碼省略
}

恩,代碼很簡單,就是在mDataSetObservable.notifyChanged()中遍歷所有觀察者,並且調用它們的onChanged方法。

那麼這些觀察者是從哪裡來的呢?首先ListView通過setAdapter方法來設置Adapter,我們看看相關代碼。

    @Override
    public void setAdapter(ListAdapter adapter) {
        // 如果已經有了一個adapter,那麼先注銷該Adapter對應的觀察者
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        // 代碼省略

        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            // 獲取數據的數量
            mItemCount = mAdapter.getCount();
            checkFocus();
            // 注意這裡 : 創建一個一個數據集觀察者
            mDataSetObserver = new AdapterDataSetObserver();
            // 將這個觀察者注冊到Adapter中,實際上是注冊到DataSetObservable中
            mAdapter.registerDataSetObserver(mDataSetObserver);

            // 代碼省略
        } else {
            // 代碼省略
        }

        requestLayout();
    }

可以看到在設置Adapter時會構建一個AdapterDataSetObserver,這不就是我們上面所說的觀察者麼,最後將這個觀察者注冊到adapter中,這樣我們的被觀察者、觀察者都有了。一般來說我們的數據集會放到Adapter中,例如 :

public abstract class UserAdapter extends BaseAdapter {
    // 數據集
    protected List<String> mDataSet = new LinkedList<String>();
    protected Context mContext = null;

    public CommonAdapter(Context context, List<String> dataSet) {
        this.mDataSet = dataSet;
        this.mContext = context;
    }
}

這個時候可能你就有點暈了? AdapterDataSetObserver是什麼?它是如何運作的?那麼我就先來看看AdapterDataSetObserver吧。

AdapterDataSetObserver定義在ListView的父類AbsListView中,代碼如下 :

    class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            if (mFastScroller != null) {
                mFastScroller.onSectionsChanged();
            }
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            if (mFastScroller != null) {
                mFastScroller.onSectionsChanged();
            }
        }
    }

它由繼承自AbsListView的父類AdapterView的AdapterDataSetObserver, 代碼如下 :

    class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;
        // 上文有說道,調用Adapter的notifyDataSetChanged的時候會調用所有觀察者的onChanged方法,核心實現就在這裡
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            // 獲取Adapter中數據的數量
            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();
            // 重新布局ListView、GridView等AdapterView組件
            requestLayout();
        }

        // 代碼省略

        public void clearSavedState() {
            mInstanceState = null;
        }
    }

到這裡我們就知道了,當ListView的數據發生變化時,調用Adapter的notifyDataSetChanged函數,這個函數又會調用DataSetObservable的notifyChanged函數,這個函數會調用所有觀察者 (AdapterDataSetObserver) 的onChanged方法。這就是一個觀察者模式!

最後我們再捋一捋,AdapterView中有一個內部類AdapterDataSetObserver,在ListView設置Adapter時會構建一個AdapterDataSetObserver,並且注冊到Adapter中,這個就是一個觀察者。而Adapter中包含一個數據集可觀察者DataSetObservable,在數據數量發生變更時開發者手動調用AdapternotifyDataSetChanged,而notifyDataSetChanged實際上會調用DataSetObservable的notifyChanged函數,該函數會遍歷所有觀察者的onChanged函數。在AdapterDataSetObserver的onChanged函數中會獲取Adapter中數據集的新數量,然後調用ListView的requestLayout()方法重新進行布局,更新用戶界面。

img1 img1 圖1 圖2

4. 雜談

ListView主要運用了Adapter和觀察者模式使得可擴展性、靈活性非常強,而耦合度卻很低,這是我認為設計模式在Android源碼中優秀運用的典范。那麼為什麼Android架構師們會這麼設計ListView,它們如何達到低耦合、高靈活性呢?這個留給大家思考吧,如果有時間我再分享我的看法。

優點

  • 觀察者和被觀察者之間是抽象耦合

缺點

  • 觀察者模式需要考慮一下開發效率和運行效率問題,一個被觀察者,多個觀察者,開發和調試就會比較復雜,而且在 Java 中消息的通知默認是順序執行,一個觀察者卡殼,會影響整體的執行效率。在這種情況下,一般考慮采用異步的方式。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved