觀察者設計模式在 RecyclerView 中的應用
簡單的理解觀察者設計模式
觀察者設計模式是行為型模式中的一種,它定義了一種一對多的關係,使的一個物件發生改變的同時能夠同步修改所有依賴它的物件,在觀察者設計模式中有兩個比較重要的概念“觀察者(Observer)”“被觀察者(Observable)”,我們從日常生活中理解它們,被觀察者可以看作是微信公眾號,觀察者也就是訂閱了微信公眾號的使用者,而公眾號的資訊也只能推送給訂閱了此公眾號的使用者,所以被觀察者也就提供了訂閱的功能供觀察者們來訂閱,如果不想接收來自被觀察者的資訊,取消訂閱即可。
在日常開發過程中,如果你的系統中發生的改變需要同步通知給其它的系統,那麼此時可以運用到觀察者設計模式,在 Android 原始碼中典型的有 RecyclerView 和 ListView 資料發生改變的同時通知列表介面來重新整理介面,帶著怎麼實現一個簡單觀察者的疑問,我們首先寫一個上文中提到的“微信公眾號小功能”。
首先是定義我們的被觀察者,即微信公眾號,微信公眾號有啥功能呢 ? 肯定是訂閱、解除訂閱以及推送訊息的功能,ok ..首先是定義一個 Observable 來將我們這些最基本的功能封裝起來,注意這裡的 Observable 和 Observer 都是自定義的不是 JDK 自帶的,千萬不要混淆了。
/** * @param <T> 使用泛型 T 來表示觀察者物件,觀察者物件實現了 Observer 介面用來規範觀察者物件所具備的行為,寫法不是固定的,你可以自由發揮 * @param <M> 使用泛型 M 來表示所傳遞的訊息物件,泛型還不會的需要自行補課去了哈 */ public class Observable<T extends Observer<M>,M> { private List<T> observerList; public Observable() { observerList = new ArrayList<>(); } /** * 用一個集合來維護所有的觀察者物件,註冊的同時就是將觀察者物件新增到集合中管理的過程 */ public void register(T t) { if (!observerList.contains(t)) { observerList.add(t); } } /** * 登出的過程就是將從集合中移除的過程 */ public void unRegister(T t) { if (observerList.contains(t)) { observerList.remove(t); } } /** * 推送訊息給所有註冊的觀察者 */ public void pushInfo(M m) { for (T t : observerList) { t.update(m); } } }
定義觀察者介面,其實不用介面也可以不過可能會損失一部分的程式碼靈活性。
public interface Observer<M> { void update(M m); }
public class Information { public String info; public Information(String info) { this.info = info; } }
到此為止我們的兩大主題都有了接下來可以去定義具體的觀察者和被觀察者了
public class PeopleObserver implements Observer<Information> { @Override public void update(Information information) { System.out.println(" 卡哇伊 我是"+information.info); } }
/** * "微信公眾號"具體被觀察者物件 */ public class WXObservable extends Observable<PeopleObserver,Information>{ }
//定義具體被觀察者物件 WXObservable wxObservable = new WXObservable(); //定義觀察者物件 PeopleObserver peopleObserver01 = new PeopleObserver(); PeopleObserver peopleObserver02 = new PeopleObserver(); //註冊 wxObservable.register(peopleObserver01); wxObservable.register(peopleObserver02); //修改資料 wxObservable.pushInfo(new Information("哈哈")); 最終的測試結果: 卡哇伊 我是 com.example.observer.simple03.PeopleObserver@2503dbd3哈哈 卡哇伊 我是 com.example.observer.simple03.PeopleObserver@4b67cf4d哈哈
以上便是觀察者模式最簡單的實現,可以看到 WXObservable 依賴並管理了一個集合,在這個集合中則是通過“註冊”新增到其中的 PeopleObserver 物件,PeopleObserver 中維護了一個 update 方法,用來在接收到資料後操作,WXObservable 推送的過程其實就是遍歷所有它維護的 PeopleObserver 物件,並呼叫 PeopleObserver 的 update 方法,是不是非常像介面回撥 ?我自己認為介面回撥就是觀察者模式的一種體現。
觀察者設計模式在 RecyclerView 中的應用
上面簡單的介紹了觀察者模式的實現,大家應該對這種設計模式有一點基本的概念了,但實際上在 Android 原始碼中處處體現著設計模式的思想,就比如說 ListView 和 RecyclerView 是如何在資料來源更新後去更新 UI 介面的顯示的,這次主要看下觀察者模式是如何在 RecyclerView 中體現的。
public void setAdapter(@Nullable RecyclerView.Adapter adapter) { this.setLayoutFrozen(false); this.setAdapterInternal(adapter, false, true); this.processDataSetCompletelyChanged(false); this.requestLayout(); }
為了搞清楚資料是怎麼通知介面上更新的,我們先從 setAdapter 函式入手,如上程式碼,還看不出什麼東西,方法中只有 setAdapterInternal 方法與 adapter 有點關係,點進去看看。
private void setAdapterInternal(@Nullable RecyclerView.Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews) { if (this.mAdapter != null) { // 1 看我 看我 this.mAdapter.unregisterAdapterDataObserver(this.mObserver); this.mAdapter.onDetachedFromRecyclerView(this); } //省略程式碼 if (adapter != null) { // 2 看我 看我 adapter.registerAdapterDataObserver(this.mObserver); adapter.onAttachedToRecyclerView(this); } if (this.mLayout != null) { this.mLayout.onAdapterChanged(oldAdapter, this.mAdapter); } this.mRecycler.onAdapterChanged(oldAdapter, this.mAdapter, compatibleWithPrevious); this.mState.mStructureChanged = true; }
看我程式碼中的備註,有沒有注意到熟悉的地方,一個是解除註冊,一個是註冊觀察者的方法,這些方法封裝在 RecyclerView.Adapter 中,所以我們跟著程式碼看看 adapter 中做了什麼處理。
public abstract static class Adapter<VH extends RecyclerView.ViewHolder> { private final RecyclerView.AdapterDataObservable mObservable = new RecyclerView.AdapterDataObservable(); private boolean mHasStableIds = false; public Adapter() { } @NonNull public abstract VH onCreateViewHolder(@NonNull ViewGroup var1, int var2); public abstract void onBindViewHolder(@NonNull VH var1, int var2); public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) { this.onBindViewHolder(holder, position); } //省略程式碼 public void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) { this.mObservable.registerObserver(observer); } public void unregisterAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) { this.mObservable.unregisterObserver(observer); } public final void notifyDataSetChanged() { this.mObservable.notifyChanged(); } public final void notifyItemChanged(int position) { this.mObservable.notifyItemRangeChanged(position, 1); } public final void notifyItemChanged(int position, @Nullable Object payload) { this.mObservable.notifyItemRangeChanged(position, 1, payload); } public final void notifyItemRangeChanged(int positionStart, int itemCount) { this.mObservable.notifyItemRangeChanged(positionStart, itemCount); } public final void notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) { this.mObservable.notifyItemRangeChanged(positionStart, itemCount, payload); } public final void notifyItemInserted(int position) { this.mObservable.notifyItemRangeInserted(position, 1); } public final void notifyItemMoved(int fromPosition, int toPosition) { this.mObservable.notifyItemMoved(fromPosition, toPosition); } public final void notifyItemRangeInserted(int positionStart, int itemCount) { this.mObservable.notifyItemRangeInserted(positionStart, itemCount); } public final void notifyItemRemoved(int position) { this.mObservable.notifyItemRangeRemoved(position, 1); } public final void notifyItemRangeRemoved(int positionStart, int itemCount) { this.mObservable.notifyItemRangeRemoved(positionStart, itemCount); } }
Adapter 是 RecyclerView 中的一個靜態內部類,在其內部例項化了被觀察者物件即 AdapterDataObservable ,所以說在初始化 Adapter 物件的時候就已經建立了被觀察者 AdapterDataObservable 物件,而在 setAdapter 時其實就是在將 觀察者物件 RecyclerViewDataObserver 與 AdapterDataObservable 兩者繫結在了一起,RecyclerViewDataObserver 是在構造方法中初始化的,程式碼我就不貼了,呼叫 adapter 的 notifyDataSetChanged 方法會回撥 AdapterDataObservable 的 notifyChanged 方法。
public void notifyChanged() { for(int i = this.mObservers.size() - 1; i >= 0; --i) { ((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onChanged(); } }
接下來是常規操作,遍歷繫結的每一個觀察者物件並同時回撥它們的 onChanged 方法,AdapterDataObserver 是一個抽象類,所以來看它的具體實現子類 RecyclerViewDataObserver
private class RecyclerViewDataObserver extends RecyclerView.AdapterDataObserver { RecyclerViewDataObserver() { } public void onChanged() { RecyclerView.this.assertNotInLayoutOrScroll((String)null); RecyclerView.this.mState.mStructureChanged = true; RecyclerView.this.processDataSetCompletelyChanged(true); if (!RecyclerView.this.mAdapterHelper.hasPendingUpdates()) { RecyclerView.this.requestLayout(); } } // 省略程式碼 }
依舊只關注它的 onChanged 方法會發現最終回調了 RecyclerView 的 requestLayout 方法,到這裡有沒有理解呢 ? 其實資料來源發生改變並呼叫 notifyDataSetChanged 方法重新整理介面的時候就是利用觀察者模式來進行訊息傳遞並最終通知我們的 View 來進行重新整理介面的操作。