跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java算法

Android 设计模式:观察者模式详解

综述由AI生成详细讲解了 Android 开发中的观察者模式,包括其基本概念、角色定义及 Java API 实现。文章通过自定义类示例展示了主题与观察者的交互流程,并深入分析了 Android 源码中 ListView 与 Adapter 如何利用观察者模式实现数据刷新机制。此外,还补充了 RxJava、LiveData 等主流框架对该模式的应用,以及在实际开发中防止内存泄漏的最佳实践。

清心发布于 2025/2/7更新于 2026/6/324 浏览
Android 设计模式:观察者模式详解

观察者模式在实际项目中使用的也是非常频繁的,它最常用的地方是 GUI 系统、订阅——发布系统等。因为这个模式的一个重要作用就是解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以 GUI 系统来说,应用的 UI 具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也经常性变化,但是业务逻辑基本变化不大,此时,GUI 系统需要一套机制来应对这种情况,使得 UI 层与具体的业务逻辑解耦,观察者模式此时就派上用场了。

概述

观察者模式又被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

模式中的角色

Android 设计模式观察者模式结构图

抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。

具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。

抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。

具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。

  1. Subject 和 Observer 是一个一对多的关系,也就是说观察者只要实现 Observer 接口并把自己注册到 Subject 中就能够接收到消息事件;
  2. Java API 有内置的观察者模式类:java.util.Observable 类和 java.util.Observer 接口,这分别对应着 Subject 和 Observer 的角色;
  3. 使用 Java API 的观察者模式类,需要注意的是被观察者在调用 notifyObservers() 函数通知观察者之前一定要调用 setChanged() 函数,要不然观察者无法接到通知;
  4. 使用 Java API 的缺点也很明显,由于 Observable 是一个类,java 只允许单继承的缺点就导致你如果同时想要获取另一个父类的属性时,你只能选择适配器模式或者是内部类的方式,而且由于 setChanged() 函数为 protected 属性,所以你除非继承 Observable 类,否则你根本无法使用该类的属性,这也违背了设计模式的原则:多用组合,少用继承。

观察者模式示例

例如:MyPerson 是被观察者

public class MyPerson extends Observable {

    private String name;
    private int age;
    private String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        setChanged();
        notifyObservers();
    }

    public   {
         age;
    }

       {
        .age = age;
        setChanged();
        notifyObservers();
    }

     String  {
         sex;
    }

       {
        .sex = sex;
        setChanged();
        notifyObservers();
    }

    
     String  {
          + name +  + age +  + sex + ;
    }
}
int
getAge
()
return
public
void
setAge
(int age)
this
public
getSex
()
return
public
void
setSex
(String sex)
this
@Override
public
toString
()
return
"MyPerson [name="
", age="
", sex="
"]"

setChanged();告知数据改变,通过 notifyObservers();发送信号通知观察者。

MyObserver 是观察者

public class MyObserver implements Observer {

    private int id;
    private MyPerson myPerson;

    public MyObserver(int id) {
        System.out.println("我是观察者---->" + id);
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public MyPerson getMyPerson() {
        return myPerson;
    }

    public void setMyPerson(MyPerson myPerson) {
        this.myPerson = myPerson;
    }

    @Override
    public void update(Observable observable, Object data) {
        System.out.println("观察者---->" + id + "得到更新");
        this.myPerson = (MyPerson) observable;
        System.out.println(((MyPerson) observable).toString());
    }

}

观察者接受到通知后,调用 update 方法进行更新操作。

public class MainActivity extends Activity {

    private Button btnAddObserver;
    private Button btnChangeData;
    private MyPerson observable;
    private MyObserver myObserver;
    private List<MyObserver> myObservers;
    private ListView listview;

    private int i;

    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            MyListAdapter myListAdapter = new MyListAdapter(MainActivity.this,
                    myObservers);
            listview.setAdapter(myListAdapter);

        };
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnAddObserver = (Button) findViewById(R.id.btn_add_observer);
        btnChangeData = (Button) findViewById(R.id.btn_change_data);
        listview = getListView();

        observable = new MyPerson();
        myObservers = new ArrayList<MyObserver>();

        btnAddObserver.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                myObserver = new MyObserver(i);
                i++;
                observable.addObserver(myObserver);
                myObservers.add(myObserver);
                handler.sendEmptyMessage(0);
            }
        });

        btnChangeData.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                observable.setName("a" + i);
                observable.setAge(10 + i);
                observable.setSex("男" + i);
                handler.sendEmptyMessage(0);
            }
        });

    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        observable.deleteObserver(myObserver);
    }
}

Android 源码中的模式实现

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

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

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() 方法

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

@Override
public void setAdapter(ListAdapter adapter) {
    // 如果已经有了一个 adapter,那么先注销该 Adapter 对应的观察者
    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }

    // 代码省略

    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 中,代码如下:

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();
        }
    }
}

它由继承自 AbsListView 的父类 AdapterView 的 AdapterDataSetObserver,代码如下:

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;
    }
}

当 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() 方法重新进行布局,更新用户界面。

常见开源框架实现

比较知名的使用观察者模式的开源框架有 RxJava、LiveData、EventBus 等。这些框架在底层大多基于此模式实现事件分发与数据监听机制。

RxJava 通过 Observable 和 Observer 接口实现了响应式编程,支持异步流处理。LiveData 作为 Jetpack 的一部分,利用观察者模式确保 UI 组件在数据变化时安全更新,且遵循生命周期感知原则。EventBus 则提供了一种轻量级的发布订阅机制,用于组件间解耦通信。

最佳实践与注意事项

在使用观察者模式时,特别是在 Android 开发中,需要注意内存泄漏问题。如果观察者持有了 Context 的强引用,且未及时注销,会导致 Context 无法被 GC 回收。建议使用 WeakReference 持有 Context,或者在 Activity/Fragment 销毁时主动移除观察者。此外,对于高频通知场景,应评估线程切换开销,避免主线程阻塞。

模式总结

优点

观察者模式解除了主题和具体观察者的耦合,让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。

缺点

依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。

适用场景

当一个对象的改变需要给变其它对象时,而且它不知道具体有多少个对象有待改变时。

一个抽象某型有两个方面,当其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。

目录

  1. 概述
  2. 模式中的角色
  3. 观察者模式示例
  4. Android 源码中的模式实现
  5. 常见开源框架实现
  6. 最佳实践与注意事项
  7. 模式总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AI 办公实战指南:7 套书籍助你精准提效与职场进阶
  • 基于 SpringBoot+Vue 的旅游网站管理系统设计与实现
  • MySQL 权限管理与 C/C++ 客户端开发实战指南
  • 大模型落地困境分析与解决思路
  • HarmonyOS NEXT WebView 套壳应用开发与文件上传问题解决
  • 基于 Spring Boot 3.3 的 Java AI Agent Gateway 实践
  • Java 工程项目管理系统功能模块与技术架构说明
  • 基于历史学习的拥塞控制算法
  • ChatLaw 袁粒:法律大模型如何助力个人维权与行业思考
  • Go Web 开发核心理论:HTTP、数据库与模板实战
  • 基于 Flask 的职位数据采集与可视化分析系统设计
  • 默认安全治理实践:水平越权检测与前端安全防控
  • Go、Rust、Kotlin、Python 与 Java 五大主流语言性能与生态对比
  • C++ 函数重载:核心规则、实现机制与实战案例
  • 基于 cpolar 内网穿透远程访问 Open-Lovable 网页克隆工具
  • 2025 年开源软件架构图生成工具指南
  • 世界模型发展脉络整理:理解世界还是预测未来?
  • Mac 本地使用 Docker 部署 n8n 并配置中文界面
  • Moyin Creator(魔因漫创):AI 影视生产级全流程创作工具
  • OpenClaw 多机器人多 Agent 模式解析

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online