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)用了觀察者模式。