觀察者模式是軟體設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實時事件處理系統。 摘自維基百科
首先,我在這裡先提出本文的幾個問題
- 什麼是觀察者模式?
RecyclerView
的Adpter.notifyDataSetChanged
為什麼能重新整理介面?RecyclerView
的觀察者,在哪裡被註冊和登出的呢?
什麼是觀察者模式?
可能對於部分初學者來說,還不是很明白觀察者模式是什麼,那麼接下來我會用簡單的例子來描述一下觀察者模式.
被觀察者(目標物件)
負責管理所有在這裡註冊過的觀察者,常常可以通過遍歷觀察者的集合來呼叫所有的觀察者.
// 被觀察者
public interface Observable<T>{
void register(Observer<T> observer);
void unregister(Observer<T> observer);
void notifyObserver();
}
複製程式碼
這裡我們可以將學生系統作為它的具體實現類,學生將在這裡被註冊
public class StudentSystem implements Observable<String> {
private List<Observer<String>> observers = new ArrayList<>();
//學生(觀察者)註冊學籍的方法
@Override
public void register(Observer<String> observer) {
System.out.println(observer+"註冊了學籍");
observers.add(observer);
}
//學生(觀察者)登出學籍的方法
@Override
public void unregister(Observer<String> observer) {
if(observers.contains(observer)){
System.out.println(observer+"登出了學籍");
observers.remove(observer);
}
}
//學生系統的全員廣播,通知學生(觀察者)
@Override
public void notifyObserver() {
for (Observer<String> observer : observers) {
observer.update("系統提示:"+observer+"馬上開學了!");
}
}
}
複製程式碼
觀察者
觀察者會將聯絡方式(引用)留給被觀察者以便於能很方便通知到它.
public interface Observer<T> {
void update(T msg);//每個觀察者被通知的入口
}
複製程式碼
同樣,在這裡我們將學生作為觀察者,則update
方法是學生系統向學生通知的途徑.
public class Student implements Observer<String> {
@Override
public void update(String msg) {
System.out.println(msg);
}
}
複製程式碼
這樣我們就構成了最簡單的觀察者
public class Main {
public static void main(String[] args) {
Student stu1 = new Student();
Student stu2 = new Student();
Student stu3 = new Student();
StudentSystem system = new StudentSystem();
system.register(stu1);
system.register(stu2);
system.register(stu3);
system.notifyObserver();
system.unregister(stu1);
system.unregister(stu2);
system.unregister(stu3);
}
}
複製程式碼
輸出:
Student@2503dbd3 註冊了學籍
Student@4b67cf4d 註冊了學籍
Student@7ea987ac 註冊了學籍
系統提示: Student@2503dbd3 馬上開學了!
系統提示: Student@4b67cf4d 馬上開學了!
系統提示: Student@7ea987ac 馬上開學了!
Student@2503dbd3 登出了學籍
Student@4b67cf4d 登出了學籍
Student@7ea987ac 登出了學籍
複製程式碼
小小總結一下
- 觀察者把自己的引用留給了被觀察者,便於被觀察者使用觀察者的方法.
- JDK中自帶了觀察者模式,不過被觀察者
(Obeservable)
是一個類,不方便使用 - 觀察者模式在Android中使用得特別多,著名的
RecyclerView
和BroadcastReceiver
等均使用了觀察者模式
回到正題——RecyclerView中的應用
只要是一個Android開發者,我想每一個人都用過RecyclerView
吧!這是Google提供的十分優秀的大量資料展示的新控制元件,其中的設計十分地精妙,運用了大量的設計模式與程式設計思想.
在我去年剛開始學習Andorid的時候,我就一直在想RecyclerView
的Adapter
為什麼呼叫notifyDataSetChange
就能重新整理所有的Item
呢?由於當時未能養成一種閱讀原始碼的習慣,只能將這個疑惑放下.
不過接下來,我將帶領大家探索一下這個問題的謎底
RecyclerView
的Adpter.notifyDataSetChanged
為什麼能重新整理介面?
先看看RecyclerView
和Adapter
的關係
由於RecyclerView的類實在是太龐大了,有足足7000多行,所以說這裡擷取小部分.可以看到adapter是RecyclerView中的一個靜態內部類
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {
//省略無數程式碼...
public abstract static class Adapter<VH extends RecyclerView.ViewHolder> {
//這個物件很關鍵,後文會講
private final RecyclerView.AdapterDataObservable mObservable = new RecyclerView.AdapterDataObservable();
//省略無數程式碼...
}
}
複製程式碼
直擊重點:Adpter.notifyDataSetChanged
public final void notifyDataSetChanged() {
this.mObservable.notifyChanged();
}
複製程式碼
這裡呼叫了mObservable
的notifyChanged
方法,而mObservable在上面已經給出來了,是RecyclerView.AdapterDataObservable
的物件,所以說我們繼續深究.
AdapterDataObservable
static class AdapterDataObservable extends Observable<RecyclerView.AdapterDataObserver> {
AdapterDataObservable() {
}
//...省略部分程式碼
public void notifyChanged() {
for(int i = this.mObservers.size() - 1; i >= 0; --i) {
((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onChanged();
}
}
//...省略部分程式碼
}
複製程式碼
顯而易見,這是一個JDK提供的Observable
的子類,而這個類的內部維護著一個ArrayList
組成的集合,用於儲存觀察者物件.
所以說,notifyChanged
方法裡,就是遍歷這個被觀察者持有的觀察者物件,並呼叫它的onChanged
方法
onChanged
我們按著ctrl鍵點進這個方法,發現這是一個抽象靜態類
public abstract static class AdapterDataObserver {
public AdapterDataObserver() {
}
public void onChanged() {
}
public void onItemRangeChanged(int positionStart, int itemCount) {
}
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
this.onItemRangeChanged(positionStart, itemCount);
}
public void onItemRangeInserted(int positionStart, int itemCount) {
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
}
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
}
}
複製程式碼
我們順藤摸瓜,於是找到了它的實現類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();
}
}
//...省略部分程式碼
}
複製程式碼
真相大白:requestLayout()
在這個實現類中,onChanged的內容就是那麼的美麗~終於解除了我學習Android一年以來的心病,就是這個方法就是更新佈局的關鍵
RecyclerView.this.requestLayout();//請求重新繪製介面
複製程式碼
最後,我們發現這裡呼叫了View的requestLayout方法,確實是請求重新繪製介面.不過這已經是View相關的內容,與本文無關了.有興趣的朋友可以繼續深入瞭解
public void requestLayout() {
if (this.mInterceptRequestLayoutDepth == 0 && !this.mLayoutFrozen) {
super.requestLayout();
} else {
this.mLayoutWasDefered = true;
}
}
複製程式碼
總結一下RecyclerView更新介面
這裡運用了觀察者模式,通過被觀察者AdapterDataObservable
遍歷每一個觀察者RecyclerViewDataObserver
,然後找到它的onChanged()
方法,利用requestLayout()
更新整個RecyclerView.
RecyclerView
的觀察者模式,在哪裡被註冊的?
又是這熟悉的一段程式碼,既然它在Recyclerview的Adapter中,那麼觀察者模式肯定和Adapter的初始化有著千絲萬縷的聯絡.
private final RecyclerView.AdapterDataObservable mObservable = new RecyclerView.AdapterDataObservable();
複製程式碼
setAdapter
於是,我們從最開始的setAdapter方法開始看,畢竟這是Adapter的入口嘛
public void setAdapter(@Nullable RecyclerView.Adapter adapter) {
this.setLayoutFrozen(false);
this.setAdapterInternal(adapter, false, true);
this.processDataSetCompletelyChanged(false);
this.requestLayout();
}
複製程式碼
在這裡,與Adapter相關的只有this.setAdapterInternal(adapter, false, true);
這一行程式碼,我敢肯定,祕密就在這一行程式碼中!
setAdapterInternal
private void setAdapterInternal(@Nullable RecyclerView.Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews) {
if (this.mAdapter != null) {//如果recyclerView之前設定過Adapter了
//登出觀察者
this.mAdapter.unregisterAdapterDataObserver(this.mObserver);
this.mAdapter.onDetachedFromRecyclerView(this);
}
if (!compatibleWithPrevious || removeAndRecycleViews) {
this.removeAndRecycleViews();
}
this.mAdapterHelper.reset();
RecyclerView.Adapter oldAdapter = this.mAdapter;
this.mAdapter = adapter;
if (adapter != null) {
//幸福來得這麼突然?
//註冊觀察者
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;
}
複製程式碼
幸福來得這麼突然?才進入兩個方法就找到了關鍵?
對了,我們看看adapter的方法幹了啥!
registerAdapterDataObserver
public void registerAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {
this.mObservable.registerObserver(observer);
}
複製程式碼
public void unregisterAdapterDataObserver(@NonNull RecyclerView.AdapterDataObserver observer) {
this.mObservable.unregisterObserver(observer);
}
複製程式碼
原來如此,這兩個方法果然登出和註冊了觀察者!
仍然總結一下下
在RecyclerView
的setAdapter
方法中,呼叫了setAdapterInternal
.然後在setAdapterInternal
中利用adapter.registerAdapterDataObserver
方法註冊觀察者,利用adapter.unregisterAdapterDataObserver
方法登出觀察者.
最後
通過以上的學習,我們已經解答了開頭提出的三個問題了,相信大家也學到了不少. 如果有錯誤的地方歡迎指正.
雖然我只是一名大二的本科生,但是歡迎大家和我私信交流!