【Rxjava详解】(一)观察者模式的拓展

news2024/11/15 13:49:22

文章目录

    • RxJava引入
      • 扩展的观察者模式
      • RxJava的观察者模式
      • 基本实现
    • RxJava入门示例
      • Action

RxJava引入

在介绍RxJava之前先说一下Rx。全称是Reactive Extensions,直译过来就是响应式扩展

Rx基于观察者模式,它是一种编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步数据流ReactiveX.io给的定义是,Rx是一个使用可观察数据流进行异步编程的编程接口,ReactiveX结合了观察者模式、迭代器模式和函数式编程的精华。Rx已经渗透到了各个语言中,有RxJavaRxJSRxSwift等等

总结一下RxJava的作用就是:异步

但是RxJava的好处是简洁。异步操作很关键的一点是程序的简洁性,因为在调度过程比较复杂的情况下,异步代码经常会既难写也难被读懂。 Android的AsyncTaskHandler其实都是为了让异步代码更加简洁。虽然RxJava的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。

扩展的观察者模式

RxJava的异步实现,是通过一种扩展的观察者模式来实现的。

观察者模式面向的需求是:A对象(观察者)对B对象(被观察者)的某种变化高度敏感,需要在B变化的一瞬间做出反应。

观察者不需要时刻盯着被观察者(例如A不需要每过2ms就检查一次B的状态),而是采用注册(Register)或者称为订阅(Subscribe)的方式,告诉被观察者:我需要你的某某状态,你要在它变化的时候通知我。

通用的观察者模式:

image

RxJava作为一个工具库,使用的就是通用形式的观察者模式。

RxJava的观察者模式

RxJava的基本概念:

  • Observable(可观察者,即被观察者):产生事件,例如去饭店吃饭的顾客。
  • Observer(观察者):接收事件,并给出响应动作,例如去饭店吃饭的厨房,会接受事件,并给出相应。
  • subscribe()(订阅):连接被观察者与观察者,例如去饭店吃饭的服务员。
    ObservableObserver通过subscribe() 方法实现订阅关系,从而Observable可以在需要的时候发出事件来通知Observer
  • Event(事件):被观察者与观察者沟通的载体,例如顾客点的菜。

与传统观察者模式不同,RxJava的事件回调方法除了普通事件onNext()(相当于onClick()/onEvent())之外,还定义了两个特殊的事件:onCompleted()onError():

但是RxJava与传统的观察者设计模式有一点明显不同,那就是如果一个Observerble没有任何的的Subscriber,那么这个Observable是不会发出任何事件的。

  • onCompleted(): 事件队列完结。

    RxJava不仅把每个事件单独处理,还会把它们看做一个队列。RxJava规定,当不会再有新的onNext()发出时,需要触发onCompleted() 方法作为标志。

  • onError(): 事件队列异常。
    在事件处理过程中出异常时,onError()会被触发,同时队列自动终止,不允许再有事件发出。

  • 在一个正确运行的事件序列中, onCompleted()onError()有且只有一个,并且是事件序列中的最后一个。需要注意的是onCompleted()onError() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。

RxJava的观察者模式大致如下图:

image

基本实现

基于上面的概念, RxJava的基本实现主要有三点:

  • 创建Observable

    Observable即被观察者,它决定什么时候触发事件以及触发怎样的事件RxJava使用Observable.create()方法来创建一个Observable,并为它定义事件触发规则

  • 创建Observer

    观察者,它决定事件触发的时候将有怎样的行为

    RxJava中的Observer接口的实现方式:

    Observer<String> observer = new Observer<String>() {
        @Override
        public void onNext(String s) {
            Log.d("xoliu", "Item: " + s);
        }
     
        @Override
        public void onCompleted() {
            Log.d("xoliu", "Completed!");
        }
     
        @Override
        public void onError(Throwable e) {
            Log.d("xoliu", "Error!");
         }
    };
    

    RxJava还内置了一个实现了Observer的抽象类:Subscriber

    SubscriberObserver接口进行了一些扩展,但他们的基本使用方式是完全一样的。

    Subscriber<String> subscriber = new Subscriber<String>() {
        @Override
        public void onNext(String s) {
            Log.d(tag, "Item: " + s);
        }
     
        @Override
        public void onCompleted() {
            Log.d(tag, "Completed!");
        }
     
        @Override
         public void onError(Throwable e) {
            Log.d(tag, "Error!");
        }
    };
    

    不仅基本使用方式一样,实质上,在RxJavasubscribe()过程中,Observer也总是会先被转换成一个Subscriber再使用。所以如果你只想使用基本功能,选择ObserverSubscriber是完全一样的。它们的区别对于使用者来说主要有两点:

    • onStart(): 这是Subscriber增加的方法。它会在subscribe()刚开始而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认情况下它的实现为空。需要注意的是,如果对准备工作的线程有要求(例如弹出一个显示进度的对话框,这必须在主线程执行), onStart()就不适用了,因为它总是在subscribe() 所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用doOnSubscribe()方法,具体可以在后面的文中看到。

    • unsubscribe(): 这是Subscriber所实现的另一个接口Subscription的方法,用于取消订阅。在这个方法被调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用isUnsubscribed()先判断一下状态。

      unsubscribe()这个方法很重要,因为在subscribe()之后,Observable会持有 Subscriber的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如onPause()、onStop()等方法中)调用unsubscribe()来解除引用关系,以避免内存泄露的发生。

  • 调用subscribe()方法(订阅)

    创建了一个ObservableObserver之后,再用subscribe()方法将它们联结起来:

    observable.subscribe(observer);  
    // 或者:
    observable.subscribe(subscriber);
    

    subscribe()这个方法有点怪:它看起来是observalbe订阅了observer/subscriber而不是observer/subscriber订阅了observalbe,这让人读起来有点别扭

RxJava入门示例

一个Observable可以发出零个或者多个事件,知道事件结束或者出错。每发出一个事件,就会调用它的Subscriber的onNext()方法,最后调用Subscriber.onComplete()或者Subscriber.onError()结束。

// 创建被观察者、数据源
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        // 这里传入了一个 OnSubscribe 对象作为参数。OnSubscribe 会被存储在返回的 Observable 对象中,它的作用相当于一个计划表,当 Observable      
        // 被订阅的时候,OnSubscribe 的 call() 方法会自动被调用,事件序列就会依照设定依次触发(观察者Subscriber 将会被调用三次 onNext() 和一次 onCompleted()
        // 这样,由被观察者调用了观察者的回调方法,就实现了由被观察者向观察者的事件传递,即观察者模式。
        subscriber.onNext("Hello ");
        subscriber.onNext("World !");
        subscriber.onCompleted();//结束
    }
});
// 创建观察者
Subscriber<String> subscriber = new Subscriber<String>() {
    @Override
    public void onCompleted() {
        Log.i("xoliu", "onCompleted");
    }

    @Override
    public void onError(Throwable e) {
        Log.i("xoliu", "onError");
    }

    @Override
    public void onNext(String s) {
        Log.i("xoliu", "onNext : " + s);
    }
};
// 订阅
observable.subscribe(subscriber);

一旦subscriber订阅了observableobservable就会调用subscriber对象的onNextonComplete方法,subscriber就会打印出Hello World.

subscriber(Subscriber subscriber)做了3件事:

  • 调用Subscriber.onStart()是一个准备方法。
  • 调用Observable对象中的onSubscribe.call(Subscriber)。在这里,事件发送的逻辑开始运行。从这也可以看出,在RxJava中,Observable 并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当subscribe()方法执行的时候。
  • 将传入的Subscriber作为Subscription返回。这是为了方便unsubscribe().

整个过程中对象间的关系如下图:

image

RxJava内置了很多简化创建Observable对象的函数,

  • Observable.just()用来创建只发出一个事件就结束的Observable对象
Observable<String> observable = Observable.just("Fuck u ", "World !");

接下来看看如何简化Subscriber,上面的例子中,我们其实并不关心onComplete()onError,我们只需要在onNext的时候做一些处理,这时候就可以使用Action1类。

Action

什么是Action
Action是RxJava 的一个接口,常用的有Action0Action1。虽然Action0Action1在API中使用最广泛,但RxJava是提供了多个ActionX形式的接口(例如Action2, Action3)的,它们可以被用以包装不同的无返回值的方法。

  • Action0: 它只有一个方法 call(),这个方法是无参无返回值的;由于 onCompleted() 方法也是无参无返回值的,因此 Action0 可以被当成一个包装对象,将 onCompleted() 的内容打包起来将自己作为一个参数传入 subscribe() 以实现不完整定义的回调。
  • Ation1:它同样只有一个方法 call(T param),这个方法也无返回值,但有一个参数;与 Action0 同理,由于 onNext(T obj)onError(Throwable error) 也是单参数无返回值的,因此 Action1 可以将 onNext(obj)onError(error) 打包起来传入 subscribe() 以实现不完整定义的回调
Action1<String> action1 = new Action1<String>() {
    @Override
    public void call(String s) {
        Log.i("xoliu", "Action1 call : " + s);
    }
};

Observable.subscribe()方法有一个重载版本,接受三个Action1类型的参数

image

所以上面的代码最终可以写成这样:

Observable.just("Hello ", "World !").subscribe(new Action1<String>() {
    @Override
    public void call(String s) {
        Log.i("xoliu", "call : " + s);
    }
});

这里顺便多提一些subscribe()的多个Action参数:

Action1<String> onNextAction = new Action1<String>() {
    // onNext()
    @Override
    public void call(String s) {
        Log.d(tag, s);
    }
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
    // onError()
    @Override
    public void call(Throwable throwable) {
        // Error handling
    }
};
Action0 onCompletedAction = new Action0() {
    // onCompleted()
    @Override
    public void call() {
        Log.d(tag, "completed");
    }
};

observable.subscribe(onNextAction, onErrorAction, onCompletedAction);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1237320.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4.15每日一题(连续函数在有界闭区域上求最大/小值:拉格朗日乘数法、化条件为无条件法)

方法一&#xff1a;拉格朗日乘数法&#xff08;拉格朗日乘数设的方程比较好解的时候使用&#xff09; 方法二&#xff1a;化条件为无条件 &#xff08;拉格朗日乘数设的方程不好解的时候使用&#xff09; &#xff08;1&#xff09;直角坐标方程化条件为无条件法 &#xff08;2…

【量化】一个简版单档tick数据回测框架

这是一个简易的模拟实际交易流程的回测框架&#xff0c;所使用的行情数据是单档的tick成交数据。为了实现调用者可以实现自己的交易逻辑&#xff0c;本框架预留了几个函数予以调用者能够继承类后在子类中重写以实现买入卖出信号的生成&#xff08;check_sell()和check_buy()&am…

ASO优化之关于应用类别的选择

用户可以通过应用商店内的类别&#xff0c;来发现他们想要的新应用。接下来我们来一起了解如何为应用来选择最符合最准确的类别。 1、应用的类别所显示的区域&#xff1f; 我们可以为APP选择一个主要类别和一个次要类别&#xff0c;所选择的主要类别对于该应用在应用商店曝光度…

3.9-Dockerfile实战

这一节介绍怎么将python程序打包成一个image&#xff0c;然后运行为一个container。 首先&#xff0c;创建/home/python/目录 mkdir /home/python/ 然后创建app.py文件。 vim app.py app.py文件的内容如下&#xff1a; from flask import Flaskapp Flask(__name__)app.route(…

「L2C」型行业从线索到成交,听懂客户之「声」是关键

存量经营时代下&#xff0c;营销变得越来越难。无论是稳流量&#xff0c;或是促活跃&#xff0c;转化率就是难以提升。 相比传统快消行业&#xff0c;线索型&#xff08;L2C&#xff0c;Leads to Cash&#xff09;行业因为客单价高、决策周期长、用户触点分散等特性&#xff0…

COMSOL 多场耦合仿真技术与应用”光电常见案例应用

(一)案列应用实操教学&#xff1a; 案例一 光子晶体能带分析、能谱计算、光纤模态计算、微腔腔膜求解 案例二 类比凝聚态领域魔角石墨烯的moir 光子晶体建模以及物理分析 案例三 传播表面等离激元和表面等离激元光栅等 案例四 超材料和超表面仿真设计&#xff0c;周期性超表面…

opencv-图像轮廓

轮廓可以简单认为成将连续的点&#xff08;连着边界&#xff09;连在一起的曲线&#xff0c;具有相同的颜色或者灰度。轮廓在形状分析和物体的检测和识别中很有用。 • 为了更加准确&#xff0c;要使用二值化图像。在寻找轮廓之前&#xff0c;要进行阈值化处理或者 Canny 边界检…

便携式自动气象站让你随时随地掌握天气变化

WX-BXQX12 随着科技的发展&#xff0c;人们对气象信息的掌握越来越重要。无论是在日常生活中&#xff0c;还是在农业生产、旅游出行等领域&#xff0c;了解天气变化都显得至关重要。为了满足人们对气象信息的需求&#xff0c;一款名为“便携式自动气象站”的创新产品应运而生。…

​​​​​​​3分钟实现EG网关串口连接麦格米特PLC

EG网关串口连接麦格米特PLC 前言&#xff1a;麦格米特PLC广泛应于工业控制领域&#xff0c;是一款性能高、稳定性强的PLC设备。此文档将介绍如何使用EG系列网关通过串口连接麦格米特PLC&#xff0c;并添加到EMCP物联网云平台&#xff0c;实现电脑Web页面、手机APP和微信对麦格米…

Oracle与Redis Enterprise协同,作为企业缓存解决方案

来源&#xff1a;虹科云科技 虹科干货丨Oracle与Redis Enterprise协同&#xff0c;作为企业缓存解决方案 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; 单独使用Oracle作为企业缓存数据库时&#xff0c;会出现哪些问题呢&#xff1f;使用Redis Enterprise与Oracle共…

新生儿近视:原因、科普和注意事项

引言&#xff1a; 近年来&#xff0c;新生儿近视的发病率逐渐上升&#xff0c;引起了广泛关注。新生儿近视的原因复杂&#xff0c;可能受到遗传、环境和行为等多方面因素的影响。本文将深入解析新生儿近视的原因&#xff0c;提供相关科普知识&#xff0c;并为父母和监护人提供…

Linux学习第44天:Linux 多点电容触摸屏实验(二):难忘记第一次牵你手的温存

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章的思维导图内容如下&#xff1a; 二、硬件原理图分析 三、实验程序编写 1、修改设备树 1&#xff09;、添加FT5426所使用的IO 一个复位 IO、一个中断 IO、…

代码随想录算法训练营第四十二天【动态规划part04】 | 01背包、416. 分割等和子集

01背包问题 题目链接&#xff1a; 题目页面 求解思路&#xff1a; 确定dp数组及其下标含义&#xff1a;dp[i][j] 表示从下标为 [0] 到 [i] 的物品里任意选取&#xff0c;放进容量为j的背包&#xff0c;此时的价值总和最大值确定递推公式&#xff1a; 不放物品i&#xff0c;…

常见树种(贵州省):006栎类

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、麻栎 …

suricata识别菜刀流量

一、捕获菜刀流量 payload特征&#xff1a; PHP: <?php eval($_POST[caidao]);?> ​ ASP: <%eval request(“caidao”)%> ​ ASP.NET: <% Page Language“Jscript”%><%eval(Request.Item[“caidao”],“unsafe”);%>数据包流量特征&#xff1a; …

第二证券:大盘仍有向上修复空间

大盘周二冲高轰动&#xff0c;上档均线压力显着。沪指当日小幅低开后快速反弹&#xff0c;盘中指数打破60日均线后不坚决加大。跟着午后抛压显着加重&#xff0c;指数回落并在收盘前翻绿。深成指全日走势同沪指相仿&#xff0c;跌幅高于沪指。值得一提的是&#xff0c;大盘自8月…

python数据结构与算法-13_高级排序算法-分治法

分治法 (Divide and Conquer) 很多有用的算法结构上是递归的&#xff0c;为了解决一个特定问题&#xff0c;算法一次或者多次递归调用其自身以解决若干子问题。 这些算法典型地遵循分治法的思想&#xff1a;将原问题分解为几个规模较小但是类似于原问题的子问题&#xff0c;递…

C盘变红怎么办?一个快速解决C盘快满的方法

前情提要 通常解决C盘快满的方法是&#xff1a; 找到C盘—右击选择“属性”—选择“详细信息”—卸载不常用的软件或者清除临时文件 缺点&#xff1a;成效甚微 今日重点 1.背景知识&#xff1a;微信是我们日常工作和生活都离不开的工具&#xff0c;我们每天使用微信会产生大量…

系列四、ThreadLocal的工作原理

一、内存结构图 二、工作原理 &#xff08;1&#xff09;Thread有一个类型为ThreadLocal.ThreadLocalMap threadLocals 的实例变量&#xff0c;即每个线程都有一个属于自己的ThreadLocalMap&#xff1b; &#xff08;2&#xff09;ThreadLocalMap内部维护着Entry数组&#xff0…