Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >观察者模式及在Android源码中的应用

观察者模式及在Android源码中的应用

作者头像
见得乐
发布于 2022-09-08 07:49:50
发布于 2022-09-08 07:49:50
64600
代码可运行
举报
文章被收录于专栏:LearnPathLearnPath
运行总次数:0
代码可运行

观察者模式

观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

观察者模式是一个使用率非常高的模式,它最常用在GUI系统、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如安卓的开源项目EventBus、Otto、AndroidEventBus等事件总线类的和RxJava响应式编程其核心都是使用观察者模式。

使用场景 - 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。 - 事件多级触发场景。 - 跨系统的消息交换场景,如消息队列、事件总线的处理机制。

栗子 这里举一个追剧的例子,平常为了不错过最新的电视剧我们会订阅或关注这个电视剧,当电视剧更新后会第一时间推送给我们,下来就简单实现一下。

抽象观察者类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 *  抽象观察者类,为所有具体观察者定义一个接口,在得到通知时更新自己
 */
public interface Observer {
    /**
     *  有更新
     *  
     *  @param message 消息
     */
    public void update(String message);

}

抽象被观察者类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 抽象被观察者类
 */
public interface Observable {

    /**
     * 推送消息
     * 
     * @param message 内容
     */
    void push(String message);

    /**
     * 订阅
     * 
     * @param observer 订阅者
     */
    void register(Observer observer);
}

具体的观察者类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 具体的观察者类,也就是订阅者
 */
public class User implements Observer {
    // 订阅者的名字
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + "," + message + "更新了!");

    }
}

具体的被观察者类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 *  具体的被观察者类,也就是订阅的节目
 */
public class Teleplay implements Observable{

    private List<Observer> list = new ArrayList<Observer>();//储存订阅者

    @Override
    public void push(String message) {
        for(Observer observer:list){
            observer.update(message);
        }
    }

    @Override
    public void register(Observer observer) {
        list.add(observer);
    }

}

实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Client {
    public static void main(String[] args) {
        //被观察者,这里就是用户订阅的电视剧
        Teleplay teleplay = new Teleplay();
        //观察者,这里就是订阅用户
        User user1 = new User("小明");
        User user2 = new User("小光");
        User user3 = new User("小兰");
        //订阅
        teleplay.register(user1);
        teleplay.register(user2);
        teleplay.register(user3);
        //推送新消息
        teleplay.push("xxx电视剧");
    }
}

结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
小明,xxx电视剧更新了!
小光,xxx电视剧更新了!
小兰,xxx电视剧更新了!

由上面的代码可以看出实现了一对多的消息推送,推送消息都是依赖Observer和Observable这些抽象类,而User和Teleplay完全没有耦合,保证了订阅系统的灵活性和可扩展性。

Android源码中的应用

在以前,我们最常用到的控件就是ListView了,而ListView最重要的一个点就是Adapter,在我们往ListView添加数据后,我们都会调用一个方法: notifyDataSetChanged(), 这个方法就是用到了我们所说的观察者模式。

跟进这个方法notifyDataSetChanged方法,这个方法定义在BaseAdapter中,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    // 数据集观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    // 代码省略

    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }

    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     * 当数据集用变化时通知所有观察者
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
}

可以发现,当数据发生变化时候,notifyDataSetChanged中会调用mDataSetObservable.notifyChanged()方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DataSetObservable extends Observable<DataSetObserver> {
    /**
     * Invokes onChanged on each observer. Called when the data set being observed has
     * changed, and which when read contains the new state of the data.
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // 调用所有观察者的onChanged方式
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

}

mDataSetObservable.notifyChanged()中遍历所有观察者,并且调用它们的onChanged方法。

那么这些观察者是从哪里来的呢?首先ListView通过setAdapter方法来设置Adapter

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public void setAdapter(ListAdapter adapter) {
        // 如果已经有了一个adapter,那么先注销该Adapter对应的观察者
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        // 代码省略

        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }

        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            // 获取数据的数量
            mItemCount = mAdapter.getCount();
            checkFocus();
            // 注意这里 : 创建一个数据集观察者
            mDataSetObserver = new AdapterDataSetObserver();
            // 将这个观察者注册到Adapter中,实际上是注册到DataSetObservable中
            mAdapter.registerDataSetObserver(mDataSetObserver);

            // 代码省略
        } else {
            // 代码省略
        }

        requestLayout();
    }

在设置Adapter时会构建一个AdapterDataSetObserver,最后将这个观察者注册到adapter中,这样我们的被观察者、观察者都有了。

AdapterDataSetObserver定义在ListView的父类AbsListView中,代码如下 :

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            if (mFastScroll != null) {
                mFastScroll.onSectionsChanged();
            }
        }
    }

从代码中可看出,它继承于AdapterView的内部类AdapterDataSetObserver,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;
        // 调用Adapter的notifyDataSetChanged的时候会调用所有观察者的onChanged方法,核心实现就在这里
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            // 获取Adapter中数据的数量
            mItemCount = getAdapter().getCount();

            // Detect the case where a cursor that was previously invalidated has
            // been repopulated with new data.
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            // 重新布局ListView、GridView等AdapterView组件
            requestLayout();
        }

        // 代码省略

        public void clearSavedState() {
            mInstanceState = null;
        }
    }

可见该类确实继承于观察者抽象类DataSetObserver。 当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged函数,这个函数又会调用DataSetObservable的notifyChanged函数,这个函数会调用所有观察者 (AdapterDataSetObserver) 的onChanged方法。这就是一个观察者模式!

**总结:**AdapterView中有一个内部类AdapterDataSetObserver,在ListView设置Adapter时会构建一个AdapterDataSetObserver,并且注册到Adapter中,这个就是一个观察者。而Adapter中包含一个数据集可观察者DataSetObservable,在数据数量发生变更时开发者手动调用Adapter.notifyDataSetChanged,而notifyDataSetChanged实际上会调用DataSetObservable的notifyChanged函数,该函数会遍历所有观察者的onChanged函数。在AdapterDataSetObserver的onChanged函数中会获取Adapter中数据集的新数量,然后调用ListView的requestLayout()方法重新进行布局,更新用户界面。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-11-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
观察者模式
观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
提莫队长
2019/02/21
6080
最熟悉的陌生人:ListView 中的观察者模式
张拭心 shixinzhang
2018/01/05
9590
最熟悉的陌生人:ListView 中的观察者模式
Android设计模式学习之观察者模式
观察者模式在实际项目中使用的也是非常频繁的,它最常用的地方是GUI系统、订阅——发布系统等。因为这个模式的一个重要作用就是解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以GUI系统来说,应用的UI具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也经常性变化,但是业务逻辑基本变化不大,此时,GUI系统需要一套机制来应对这种情况,使得UI层与具体的业务逻辑解耦,观察者模式此时就派上用场了。
老马的编程之旅
2022/06/22
3740
Android设计模式学习之观察者模式
观察者模式详解
从字面意思上去理解,所谓的观察者模式,首先有观察者(一个或者多个),被观察者(一个)。当被观察者状态发生变化的时候,就会去通知它的所有的观察者,然后由观察者根据被观察者的情况作出反应。观察者模式属于行为型模式。
开发者
2019/12/26
4440
观察者模式详解
Android设计模式--观察者模式
观察者模式是一个使用频率非常高的模式,他最常用的地方是GUI系统和订阅-发布系统。 该模式的最重要的作用就是解耦,使观察者和被观察者之间依赖尽可能小,甚至好无依赖。
先知先觉
2019/01/21
1.4K0
BaseAdapter的notifyDataSetChanged方法[通俗易懂]
都用过 BaseAdapter的notifyDataSetChanged()方法,用法很简单,当BaseAdapter的数据更新了,需要更改显示,这时候就要调用notifyDataSetChanged()方法来更新数据,当然你可以用一种比较恶心的方式,在你所使用的AdapterView(这里是指AdapterView的子类,ListView,GridView,Gallery等等),调setAdapter()方法。好好分析一下如何使用以及为什么要使用notifyDataSetChanged方法。
全栈程序员站长
2022/09/12
1.4K0
BaseAdapter的notifyDataSetChanged方法[通俗易懂]
观察者模式(触发联动)
目录: 1、举例:发起登录请求 2、Android Adapter 相关源代码分析 3、EventBus 相关源代码分析 4、观察者模式总结
幺鹿
2018/08/21
9570
观察者模式(触发联动)
Override ListView getAdapter造成的后果
近期工作中,发现了一个bug,是和ListView Adapter有关的。产生了FC,描写叙述信息大约是
全栈程序员站长
2021/11/24
3320
浅谈Adapter中观察者模式
首先让我们看一个客户端图片 这是京东客户端的购物车界面。有这么一种功能,当我们在商品列表中调整商品数量等信息的时候,下方的金额总数也随之变化。 可以看出,这个界面有一个数据源,一个适配器,一个List
听着music睡
2018/05/18
1.3K0
Android 设计模式之观察者模式
AntDream
2024/06/13
1820
Android 设计模式之观察者模式
设计模式学习之适配器模式
在我们平时的开发过程中,适配器模式可以说是经常能见到的设计模式,ListView 和 RecyclerView 的 Adapter 就是典型的适配器模式,当我们在开发时,碰到要在两个完全没有关系的类之间进行交互,第一个解决方案是修改各自类的接口,但是如果无法修改源代码或者其他原因导致无法更改接口,此时怎么办?这种情况我们往往会使用一个 Adapter ,在这两个接口之间创建一个粘合剂接口,将原本无法协作的类进行兼容,而且不用修改原来两个模块的代码,符合开闭原则。
老马的编程之旅
2022/06/22
4170
设计模式学习之适配器模式
PagerAdapter闪屏坑的修复
背景 最近在填前同事的一个坑时,不小心遇到另外一个坑。 在一个礼物面板,原实现是gridView + ViewPager实现的(有几页礼物),在送用户免费礼物时,刷新ViewPager里面的item时,出现了闪屏。 其实很多童鞋知道,PagerAdapter在调用notifyDataSetChanged(), 如果使用默认的会不起作用 点进notifyDataSetChanged() /** * This method should be called by the application if
用户1127566
2018/06/04
2.2K0
Android源码设计模式解析与实战笔记
1.单一职责原则:比如说一个ImageLoader,需要加载图片的缓存图片,此时如果将这两个功能都放在一个类中,就违反了这个原则, 我们需要将不同的功能用类精细组织起来,然后通过成员变量的形式将功能组合起来。 2.开闭原则:如果我们要在1的基础上增加更多的硬件缓存或者双缓存,此时如果只是在原来的类中使用if进行判断那么就违反了这个原则,因为对于一个类我们需要的是对于修改是关闭的,对于扩展是开发的,此时我们就可以将缓存类定义成抽象的接口,然后将各个缓存的实现,以多态的形式设置在ImageLoader之中,此
何时夕
2018/05/02
1.1K0
RecyclerView 刷新列表数据的 notifyDataSetChanged() 为什么是昂贵的?
当列表数据变更时,调用 notifyDataSetChanged() 是最省事的。无需关心变更的细节,一股脑统统刷一遍就完事了。但这样做也是最昂贵的。读完这一篇源码走查就知道为啥它这么昂贵了。
Rouse
2021/05/28
3.6K0
RecyclerView 刷新列表数据的 notifyDataSetChanged() 为什么是昂贵的?
7.数据库、Contentobserver
群组页是程序内部维护的一个数据库,其中一张表groups,用于存放创建的群组,还有一张表thread_group,用于关联群组和系统短信数据库中的会话。 数据库应该这样设计 MySqliteHelpe
六月的雨
2018/05/14
9760
关于RecyclerView的Adapter的notifyItemInserted()的一些分析
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/51698261
程序员徐公
2018/09/18
3.3K0
观察者模式
大家好,我是老田,今天我给大家分享设计模式中的观察者模式。用贴切的生活故事,以及真实项目场景来讲设计模式,最后用一句话来总结这个设计模式。
田维常
2021/07/15
7550
Android 常用设计模式
由于项目变更的频繁性,作为一名程序员,我们需要掌握设计模式的必要性,就不言而喻~~,下面就是一些我自己学习的设计模式总结。 接下来,主要是针对几个比较常用模式进行讲解,主要是以下几种:
te大大
2021/11/22
3890
一起学设计模式 - 观察者模式
观察者模式是一种使用率极高的模式,用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。
battcn
2018/08/03
3830
一起学设计模式 - 观察者模式
观察者模式 Observer 发布订阅模式 源 监听 行为型 设计模式(二十三)
定义对象一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖他的对象都得到通知并自动更新。
noteless
2018/12/27
6250
相关推荐
观察者模式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验