博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
观察者模式——RecyclerView中的应用
阅读量:6324 次
发布时间:2019-06-22

本文共 8251 字,大约阅读时间需要 27 分钟。

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

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

  1. 什么是观察者模式?
  2. RecyclerViewAdpter.notifyDataSetChanged为什么能刷新界面?
  3. RecyclerView的观察者,在哪里被注册和注销的呢?

什么是观察者模式?

可能对于部分初学者来说,还不是很明白观察者模式是什么,那么接下来我会用简单的例子来描述一下观察者模式.

被观察者(目标对象)

负责管理所有在这里注册过的观察者,常常可以通过遍历观察者的集合来呼叫所有的观察者.

// 被观察者public interface Observable
{ void register(Observer
observer); void unregister(Observer
observer); void notifyObserver();}复制代码

这里我们可以将学生系统作为它的具体实现类,学生将在这里被注册

public class StudentSystem implements Observable
{ private List
> observers = new ArrayList<>(); //学生(观察者)注册学籍的方法 @Override public void register(Observer
observer) { System.out.println(observer+"注册了学籍"); observers.add(observer); } //学生(观察者)注销学籍的方法 @Override public void unregister(Observer
observer) { if(observers.contains(observer)){ System.out.println(observer+"注销了学籍"); observers.remove(observer); } } //学生系统的全员广播,通知学生(观察者) @Override public void notifyObserver() { for (Observer
observer : observers) { observer.update("系统提示:"+observer+"马上开学了!"); } }}复制代码

观察者

观察者会将联系方式(引用)留给被观察者以便于能很方便通知到它.

public interface Observer
{ void update(T msg);//每个观察者被通知的入口}复制代码

同样,在这里我们将学生作为观察者,则update方法是学生系统向学生通知的途径.

public class Student implements Observer
{ @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
{ //这个对象很关键,后文会讲 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
{ 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);        }复制代码

原来如此,这两个方法果然注销和注册了观察者!

仍然总结一下下

RecyclerViewsetAdapter方法中,调用了setAdapterInternal.然后在setAdapterInternal中利用adapter.registerAdapterDataObserver方法注册观察者,利用adapter.unregisterAdapterDataObserver方法注销观察者.

最后

通过以上的学习,我们已经解答了开头提出的三个问题了,相信大家也学到了不少. 如果有错误的地方欢迎指正.

虽然我只是一名大二的本科生,但是欢迎大家和我私信交流!

转载于:https://juejin.im/post/5c6ad37c6fb9a049f43bdfa9

你可能感兴趣的文章
【.net 深呼吸】启动一个进程并实时获取状态信息
查看>>
MVC5 的MicrosoftOwinSecurity扩展插件——微信,QQ登录第三方源码
查看>>
分布式系统理论基础 - CAP
查看>>
mysql 用户管理和权限设置
查看>>
【项目管理和构建】十分钟教程,eclipse配置maven + 创建maven项目
查看>>
[转]Asp.Net大型项目实践(11)-基于MVC Action粒度的权限管理【续】【源码在这里】(在线demo,全部源码)...
查看>>
Numpy 的ndarray
查看>>
牛人博客 列表
查看>>
数据库连接池dataesoruce pool深入理解
查看>>
vuejs2.0使用Sortable.js实现的拖拽功能
查看>>
oracle多实例的启动与关闭
查看>>
码农生涯杂记_4
查看>>
利用jQuery设计横/纵向菜单
查看>>
unity游戏开发之NGUI的UISprite染色
查看>>
HDOJ find the safest road 1596【最短路变形】
查看>>
高度决定视野眼界决定世界
查看>>
shell脚本路径写法的注意点
查看>>
Testng生成的测试报告乱码解决办法
查看>>
vim快速入门
查看>>
大杂烩 -- 单向链表是否存在环或是否相交
查看>>