Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 當觀察者模式和回調機制遇上Android源碼

當觀察者模式和回調機制遇上Android源碼

編輯:關於Android編程

上一篇博客跟大家分享了Android源碼中的裝飾者模式,有點意猶未盡,今天跟大家分享下Android中的觀察者模式,順便說一說觀察者模式和回調機制的關系,歡迎大家拍磚。

觀察者模式

定義

觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。

觀察者模式的結構

\

觀察者模式所涉及的角色有:

抽象主題(Subject)角色:抽象主題角色把所有對觀察者對象的引用保存在一個聚集(比如ArrayList對象)裡,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象,抽象主題角色又叫做抽象被觀察者(Observable)角色。

具體主題(ConcreteSubject)角色:將有關狀態存入具體觀察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者(Concrete Observable)角色。  

抽象觀察者(Observer)角色:為所有的具體觀察者定義一個接口,在得到主題的通知時更新自己,這個接口叫做更新接口。
  

具體觀察者(ConcreteObserver)角色:存儲與主題的狀態自恰的狀態。具體觀察者角色實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態 像協調。如果需要,具體觀察者角色可以保持一個指向具體主題對象的引用。

實現

抽象主題角色類

public abstract class Subject {
    /**
     * 用來保存注冊的觀察者對象
     */
    private    List list = new ArrayList();
    /**
     * 注冊觀察者對象
     * @param observer    觀察者對象
     */
    public void attach(Observer observer){

        list.add(observer);
        System.out.println("Attached an observer");
    }
    /**
     * 刪除觀察者對象
     * @param observer    觀察者對象
     */
    public void detach(Observer observer){

        list.remove(observer);
    }
    /**
     * 通知所有注冊的觀察者對象
     */
    public void nodifyObservers(String newState){

        for(Observer observer : list){
            observer.update(newState);
        }
    }
}

具體主題角色類

public class ConcreteSubject extends Subject{

    private String state;

    public String getState() {
        return state;
    }

    public void change(String newState){
        state = newState;
        System.out.println("主題狀態為:" + state);
        //狀態發生改變,通知各個觀察者
        this.nodifyObservers(state);
    }
}

抽象觀察者角色類

public interface Observer {
    /**
     * 更新接口
     * @param state    更新的狀態
     */
    public void update(String state);
}

具體觀察者角色類

public class ConcreteObserver implements Observer {
    //觀察者的狀態
    private String observerState;

    @Override
    public void update(String state) {
        /**
         * 更新觀察者的狀態,使其與目標的狀態保持一致
         */
        observerState = state;
        System.out.println("狀態為:"+observerState);
    }

}

測試類

public class Test {

    public static void main(String[] args) {
        //創建主題對象
        ConcreteSubject subject = new ConcreteSubject();
        //創建觀察者對象
        Observer observer = new ConcreteObserver();
        //將觀察者對象登記到主題對象上
        subject.attach(observer);
        //改變主題對象的狀態
        subject.change("new state");
    }

}

觀察者的兩種實現方式

Push

主題對象向觀察者推送主題的詳細信息,不管觀察者是否需要,推送的信息通常是主題對象的全部或部分數據。

Pull

主題對象在通知觀察者的時候,只傳遞少量信息。如果觀察者需要更具體的信息,由觀察者主動到主題對象中獲取,相當於是觀察者從主題對象中拉數據。一般這種模型的實現中,會把主題對象自身通過update()方法傳遞給觀察者,這樣在觀察者需要獲取數據的時候,就可以通過這個引用來獲取了。

兩種方式的比較

Push模型是假定主題對象知道觀察者需要的數據;而Pull模型是主題對象不知道觀察者具體需要什麼數據,沒有辦法的情況下,干脆把自身傳遞給觀察者,讓觀察者自己去按需要取值。

Push模型可能會使得觀察者對象難以復用,因為觀察者的update()方法是按需要定義的參數,可能無法兼顧沒有考慮到的使用情況。這就意味著出現新情況的時候,就可能提供新的update()方法,或者是干脆重新實現觀察者;而Pull模型就不會造成這樣的情況,因為Pull模型下,update()方法的參數是主題對象本身,這基本上是主題對象能傳遞的最大數據集合了,基本上可以適應各種情況的需要。

回調機制和觀察者模式

Android中有非常多的地方使用了回調機制,例如Activity的生命周期、按鈕的點擊事件、線程的run()方法等。

下面是回調的基本模型:

public interface CallBack {  
    public void oncall();  
}  

public class A {  
    private CallBack callback;  
    //注冊一個事件  
    public void register(CallBack callback){  
        this.callback = callback;  
    }  
    // 需要調用的時候回調  
    public void call(){  
        callback.oncall();  
    }  
}  

public static void main(String[] args) {  
    A a = new A();  
    a.register(new CallBack() {  
        @Override  
        public void oncall() {  
            System.out.println("回調函數被調用");  
        }  
    });  
    a.call();  

}

這樣看來,回調機制和觀察者模式是一致的,區別是觀察者模式裡面目標類維護了所有觀察者的引用,而回調裡面只是維護了一個引用。

Android中的觀察者模式

Android中大量的使用了觀察者模式,Framework層裡面的事件驅動都是基於觀察者模式實現的。另外在Framework層裡面的各種服務在數據變更的時候,也是通過觀察者模式實現上層數據更新的。像View的Listener監聽、GPS位置信息監聽、BroadcastReceiver等都是基於觀察者模式實現的。下面我們說一說ListView中的觀察者模式是如何實現的,RecyclerView大同小異,感興趣的可以自己研究下。

Listview的notifyDataSetChanged()

我們先來看下listview部分觀察者模式的結構

\

其中為了方便研究關系,我們省略了Adapter部分的一些類的關系。接下來我們看下具體調用關系。

首先當我們數據改變的時候我們會調用adapter的notifyDataSetChanged()方法。

/**
 * Common base class of common implementation for an {@link Adapter} that can be
 * used in both {@link ListView} (by implementing the specialized
 * {@link ListAdapter} interface) and {@link Spinner} (by implementing the
 * specialized {@link SpinnerAdapter} interface).
 */
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    public boolean hasStableIds() {
        return false;
    }

    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();
    }

    /**
     * Notifies the attached observers that the underlying data is no longer valid
     * or available. Once invoked this adapter is no longer valid and should
     * not report further data set changes.
     */
    public void notifyDataSetInvalidated() {
        mDataSetObservable.notifyInvalidated();
    }
}

根據上述代碼我們可以定位到mDataSetObservable.notifyChanged()方法。

/**
 * A specialization of {@link Observable} for {@link DataSetObserver}
 * that provides methods for sending notifications to a list of
 * {@link DataSetObserver} objects.
 */
public class DataSetObservable extends Observable {
    /**
     * Invokes {@link DataSetObserver#onChanged} on each observer.
     * Called when the contents of the data set have changed.  The recipient
     * will obtain the new contents the next time it queries the data set.
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // 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();
            }
        }
    }

    /**
     * Invokes {@link DataSetObserver#onInvalidated} on each observer.
     * Called when the data set is no longer valid and cannot be queried again,
     * such as when the data set has been closed.
     */
    public void notifyInvalidated() {
        synchronized (mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onInvalidated();
            }
        }
    }
}

我們看到,調用notifyChanged()方法,會去遍歷mObservers,調用所有觀察者的onchange()方法。

那麼問題來了,我們的觀察者對象是什麼時候添加進去的呢?我們去看下ListView第一次和BaseAdapter產生關聯的地方,也就是setAdapter(ListAdapter adapter)方法。

@Override
public void setAdapter(ListAdapter adapter) {
    //如果已經設置過了Adapter,那麼取消注冊對應的觀察者。
    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }

    //省略部分代碼

    if (mAdapter != null) {
        mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
        mOldItemCount = mItemCount;
        mItemCount = mAdapter.getCount();
        checkFocus();

        //創建一個對應數據的觀察者
        mDataSetObserver = new AdapterDataSetObserver();
        //間接調用DataSetObservable的注冊方法
        mAdapter.registerDataSetObserver(mDataSetObserver);

        //省略部分代碼
    } else {
        mAreAllItemsSelectable = true;
        checkFocus();
        // Nothing selected
        checkSelectionChanged();
    }

    requestLayout();
}

這樣我們的四個角色就全了,Observable—>Subject;DataSetObservable—>Concrete Subject;DataSetObserver—>Observer;AdapterDataSetObserver—>Concrete Observer。然後我們注冊的地方也找到了。

最後就剩下我們的數據是如何刷新這一個問題了。AdapterDataSetObserver定義在ListView的父類AbsListView中,它又繼承自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;
    }
} 

requestLayout()方法在View裡有實現,子View按需求重寫。我們看下注釋好了。

/*Call this when something has changed which has invalidated the layout of this view. This will schedule a layout pass of the view tree./

好了,到這裡所有的調用關系我們基本就搞清楚了。當ListView的數據發生變化時,調用Adapter的notifyDataSetChanged函數,這個函數又會調用DataSetObservable的notifyChanged函數,這個函數會調用所有觀察者 (AdapterDataSetObserver) 的onChanged方法。在onChanged函數中會獲取Adapter中數據集的新數量,然後調用ListView的requestLayout()方法重新進行布局,更新用戶界面。

瞎總結

ListView主要運用了Adapter和觀察者模式使得可擴展性、靈活性非常強,而耦合度卻很低,這是我認為設計模式在Android源碼中優秀運用的典范。那我們就要開始思考了,我們有沒有其他更漂亮的套路來實現ListView組件,我們可以把這件實現思路應用到哪裡?

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved