Android源碼中多處使用觀察者模式,這里就簡單羅列幾處。
觀察者(DataSetObserver),目標(抽象主題、被觀察者)(`Observable<T>`),具體目標(具體主題,具體被觀察者)(DataSetObserverable)
**Observer(觀察者)**:DataSetObserver抽象2個方法,一個是觀察數據改變的方法,一個是觀察數據變成無效(或者不可用)時的方法。
源碼路徑:framework/base/core/java/android/database/DataSetObserver.java
~~~
package android.database;
/**
* Receives call backs when a data set has been changed, or made invalid. The typically data sets
* that are observed are {@link Cursor}s or {@link android.widget.Adapter}s.
* DataSetObserver must be implemented by objects which are added to a DataSetObservable.
*/
public abstract class DataSetObserver {
/**
* This method is called when the entire data set has changed,
* most likely through a call to {@link Cursor#requery()} on a {@link Cursor}.
*/
public void onChanged() {
// Do nothing
}
/**
* This method is called when the entire data becomes invalid,
* most likely through a call to {@link Cursor#deactivate()} or {@link Cursor#close()} on a
* {@link Cursor}.
*/
public void onInvalidated() {
// Do nothing
}
}
~~~
**被觀察者**
**Subject(目標)**:`Observable<T>`是一個泛型的抽象類,主要功能是注冊和撤銷observer。
源碼路徑:framework/base/core/java/android/database/Observable.java
~~~
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.database;
import java.util.ArrayList;
/**
* Provides methods for registering or unregistering arbitrary observers in an {@link ArrayList}.
*
* This abstract class is intended to be subclassed and specialized to maintain
* a registry of observers of specific types and dispatch notifications to them.
*
* @param T The observer type.
*/
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);
}
}
/**
* Remove all registered observers.
*/
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
~~~
**ConcreateSubject(具體目標)**:實現的方法同Oberver一樣,只不過它是通知`ArrayList<Observer>`下的每個Oberver去執行各自的action。
源碼路徑:framework/base/core/java/android/database/DataSetObservable.java
~~~
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.database;
/**
* 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<DataSetObserver> {
/**
* 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();
}
}
}
}
~~~
**ConcreateObserver(具體觀察者)**:具體觀察者的任務是實實在在執行action的類,一般由開發者根據實際情況,自己實現。android也有實現的例子
源碼路徑:framework/base/core/java/android/widget/AbsListView.java
~~~
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();
}
}
}
~~~
framework/base/core/java/android/widget/AdapterView.java-AdapterDataSetObserver.java
~~~
class AdapterDataSetObserver extends DataSetObserver {
private Parcelable mInstanceState = null;
@Override
public void onChanged() {
mDataChanged = true;
mOldItemCount = mItemCount;
mItemCount = getAdapter().getCount();
if (DBG) {
Xlog.d(TAG, "AdapterView onChanged: mOldItemCount = " + mOldItemCount
+ ",mItemCount = " + mItemCount + ",getAdapter() = " + getAdapter()
+ ",AdapterView = " + AdapterView.this, new Throwable("onChanged"));
}
// 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();
}
@Override
public void onInvalidated() {
mDataChanged = true;
if (DBG) {
Xlog.d(TAG, "AdapterView onInvalidated: mOldItemCount = " + mOldItemCount
+ ",mItemCount = " + mItemCount + ",getAdapter() = " + getAdapter()
+ ",AdapterView = " + AdapterView.this, new Throwable("onInvalidated"));
}
if (AdapterView.this.getAdapter().hasStableIds()) {
// Remember the current state for the case where our hosting activity is being
// stopped and later restarted
mInstanceState = AdapterView.this.onSaveInstanceState();
}
// Data is invalid so we should reset our state
mOldItemCount = mItemCount;
mItemCount = 0;
mSelectedPosition = INVALID_POSITION;
mSelectedRowId = INVALID_ROW_ID;
mNextSelectedPosition = INVALID_POSITION;
mNextSelectedRowId = INVALID_ROW_ID;
mNeedSync = false;
checkFocus();
requestLayout();
}
public void clearSavedState() {
mInstanceState = null;
}
}
~~~
**實例**:
型運用是大家熟悉的BaseAdapter,BaseAdapter關聯了一個DataSetObservable對象,并實現registerDataSetObserver和unregisterDataSetObserver兩個方法實現注冊和撤銷Observer,方法notifyDataSetChanged間接調用Observer的實現者的onChange()方法,以達到通知數據改變的作用。使用ListView和BaseAdapter組合時,當BaseAdapter的item改變時,我們經常會調用notifyDataSetChanged(),通知Listview刷新。
#### **BaseAdapter**
BaseAdapter 部分代碼
~~~
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);
}
/**
* 當數據集變化時,通知所有觀察者
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
}
............
}
~~~
看看mDataSetObservable.notifyChanged()方法:
~~~
/**
*數據集觀察者
*/
public class DataSetObservable extends Observable<DataSetObserver> {
/**
* 調用每個觀察者的onChanged函數來通知它們被觀察者發生了變化
*/
public void notifyChanged() {
synchronized(mObservers) {
// 調用所有觀察者的onChanged方式
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
}
...............
}
~~~
可以看出在mDataSetObservable.notifyChanged()中遍歷所有觀察者,并調用他們的onChanged(),從而告知觀察者發生了什么。
那么觀察者怎么來的,那就是setAdapter方法,其實這些觀察者就是ListView通過setAdapter方法設置Adapter產生的,代碼如下:
~~~
@Override
public void setAdapter(ListAdapter adapter) {
//如果已經有一個Adapter,那么先注銷該Adapter對應的觀察者
if (mAdapter != null && mDataSetObserver != null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
} else {
mAdapter = adapter;
}
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if (mAdapter != null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
//獲取數據的數量
mItemCount = mAdapter.getCount();
checkFocus();
//注意這里:創建一個一個數據集觀察者
mDataSetObserver = new AdapterDataSetObserver();
//將這個觀察者注冊到Adapter中,實際上是注冊到DataSetObservable中
mAdapter.registerDataSetObserver(mDataSetObserver);//注冊觀察者
......省略
}
~~~
從程序中可以看出,在設置Adapter時會構建一個AdapterDataSetObserver,這就是所謂的觀察者,最后將這個觀察者注冊到adapter中,這樣觀察者和被觀察者都有了。AdapterDataSetObserver是什么?AdapterDataSetObserver定義在ListView的父類AbsListView中,具體代碼如下
**AdapterDataSetObserver.java**
~~~
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方法,在onChanged方法中又會調用ListView重新布局的函數來刷新ListView。**這就是一個觀察者模式**!
**總結**:梳理一下整個過程,AdapterView中有一個內部類AdapterDataSetObserver,在ListView中設置Adapter時會構建一個AdapterDataSetObserver,并且注冊到Adapter中,這就是一個觀察者。而Adapter中包含一個數據集可觀察者DataSetObservable,在數據數量發生變更時,開發者手動調用Adapter的notifyDataSetChanged方法,而notifyDataSetChanged實際上會調用DataSetObservable的notifyChanged函數,該函數會遍歷所有觀察者的onChanged方法。在AdapterDataSetObserver的onChanged方法中會獲取Adapter中數據集的新數量,然后調用ListView的requestLayout()方法重新進行布局,更新用戶界面。
**其實,Android用到DataSetObserver的地方很多,Cursor,WebView,Adapter,...非常之多。**