观察者模式是软件设计模式的一种. 在此种模式中, 一个目标对象管理所有相依于它的观察者对象, 并且在它本身的状态改变时主动发出通知. 这通常透过呼叫各观察者所提供的方法来实现. 此种模式通常被用来实时事件处理系统. 摘自维基百科
首先, 我在这里先提出本文的几个问题
什么是观察者模式?
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 方法注销观察者.
最后
通过以上的学习, 我们已经解答了开头提出的三个问题了, 相信大家也学到了不少. 如果有错误的地方欢迎指正.
虽然我只是一名大二的本科生, 但是欢迎大家和我私信交流!