观察者模式——RecyclerView中的应用

栏目: 后端 · 发布时间: 5年前

内容简介:观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。 摘自维基百科首先,我在这里先提出本文的几个问题可能对于部分初学者来说,还不是很明白观察者模式是什么,那么接下来我会用简单的例子来描述一下观察者模式.

观察者模式是软件 设计模式 的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。 摘自维基百科

首先,我在这里先提出本文的几个问题

  1. 什么是观察者模式?
  2. RecyclerViewAdpter.notifyDataSetChanged 为什么能刷新界面?
  3. 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中使用得特别多,著名的 RecyclerViewBroadcastReceiver 等均使用了观察者模式

回到正题——RecyclerView中的应用

只要是一个Android开发者,我想每一个人都用过 RecyclerView 吧!这是Google提供的十分优秀的大量数据展示的新控件,其中的设计十分地精妙,运用了大量的设计模式与编程思想.

在我去年刚开始学习Andorid的时候,我就一直在想 RecyclerViewAdapter 为什么调用 notifyDataSetChange 就能刷新所有的 Item 呢?由于当时未能养成一种阅读源码的习惯,只能将这个疑惑放下.

不过接下来,我将带领大家探索一下这个问题的谜底

RecyclerViewAdpter.notifyDataSetChanged 为什么能刷新界面?

先看看 RecyclerViewAdapter 的关系

由于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();
        }
复制代码

这里调用了 mObservablenotifyChanged 方法,而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中的应用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

创造突破性产品

创造突破性产品

Jonathan Cagan、Craig M.Vogel / 机械工业出版社 / 2004-1 / 35.00元

在《创造突破性产品:从产品策略到项目定案的创新》中作者总结多年的研究成果,指明了与产品创新相关的一系列因素,并提供了一套全新的开发突破性产品的理论与方法,该书旨在帮助企业,技术和设计人员: 获得对用户的需求和市场新的趋势的准确洞察力; 认识可以创造新市场的产品机会缺口; 指导产品模糊前期的构造; 正确地运用定性和定量的研究方法; ......一起来看看 《创造突破性产品》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具