RxJava入门之介绍与基本运用

前言

因为这个RxJava内容不算少,而且应用场景非常广,所以这个关于RxJava的文章我们会陆续更新,今天就来先来个入门RxJava吧

初识RxJava

什么是Rx

很多教程在讲解RxJava的时候,上来就介绍了什么是RxJava。这里我先说一下什么是Rx,Rx就是ReactiveX,官方定义是:

Rx是一个函数库,让开发者可以利用可观察序列和LINQ风格查询操作符来编写异步和基于事件的程序

看到这个定义我只能呵呵,稍微通俗点说是这样的:

Rx是微软.NET的一个响应式扩展。Rx借助可观测的序列提供一种简单的方式来创建异步的,基于事件驱动的程序。

这个有点清晰了,至少看到我们熟悉的异步与事件驱动,所以简单点且不准确地来说:

Rx就是一种响应式编程,来创建基于事件的异步程序

注意,这个定义是不准确的,但是对于初学者来说,已经可以有个基本的认知了。

另外还有一点就是Rx其实是一种编程思想,用很多语言都可以实现,比如RxJava、RxJS、RxPHP等等。而现在我们要说的就是RxJava。

RxJava是什么

二话不说,先上定义:

RxJava就是一种用Java语言实现的响应式编程,来创建基于事件的异步程序

有人问你这不是废话么,好吧那我上官方定义:

一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库

反正我刚看这句话的时候也呵呵了,当然现在有所领悟了。

除此之外,就是:异步,它就是一个实现异步操作的库。

扩展的观察者模式

对于普通的观察者模式,这里我就不细说了。简单概括就是,观察者(Observer)需要在被观察者(Observable)变化的一瞬间做出反应。

而两者通过注册(Register)或者订阅(Subscribe)的方式进行绑定。

就拿扔物线老师给的例子来说,我丰富了一下如图所示:

其中这个Button就是被观察者(Observable),OnClickListener就是观察者(Observer),两者通过setOnClickListener达成订阅(Subscribe)关系,之后当Button产生OnClick事件的时候,会直接发送给OnClickListener,它做出相应的响应处理。

当然还有其他的例子,比如Android四大组件中的ContentProvider与ContentObserver之间也存在这样的关系。

而RxJava的观察者模式呢,跟这个差不多,但是也有几点差别:

Observer与Observable是通过 subscribe() 来达成订阅关系。

RxJava中事件回调有三种:onNext() onCompleted() onError()

如果一个Observerble没有任何的Observer,那么这个Observable是不会发出任何事件的。

其中关于第三点,这里想说明一下,在Rx中,其实Observable有两种形式:热启动Observable和冷启动Observable。

热启动Observable任何时候都会发送消息,即使没有任何观察者监听它。

冷启动Observable只有在至少有一个订阅者的时候才会发送消息

这个地方虽然对于初学者来说区别不大,但是要注意一下,所以上面的第三点其实就针对于冷启动来说的。

另外,关于RxJava的回调事件的总结:

onNext() :基本事件。

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

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

值得注意的是在一个正确运行的事件序列中, onCompleted()onError() 有且只有一个,并且是事件序列中的最后一个。如果在队列中调用了其中一个,就不应该再调用另一个。

好了,那我们也附一张图对比一下吧:

如何实现RxJava

关于实现RxJava的步骤,这里我就大体总结概括一下。

创建Observer

在Java中,一想到要创建一个对象,我们马上就想要new一个。没错,这里我们也是要new一个Observer出来,其实就是实现Observer的接口,注意String是接收参数的类型:

//创建Observer
Observer<String> observer = new Observer<String>() {
 @Override
 public void onNext(String s) {
  Log.i("onNext ---> ", "Item: " + s);
 }

 @Override
 public void onCompleted() {
  Log.i("onCompleted ---> ", "完成");
 }

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

当然这里也要提一个实现了 Observer 接口的抽象类:Subscriber ,它跟 Observer 接口几乎完全一样,只是多了两个方法,看看总结:

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

unsubscribe() : 用于取消订阅。在这个方法被调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用 isUnsubscribed() 先判断一下状态。 要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。

虽然多了两个方法,但是基本实现方式跟Observer是一样的,所以暂时可以不考虑两者的区别。不过值得注意的是:

实质上,在 RxJava 的 subscribe 过程中,Observer 也总是会先被转换成一个 Subscriber 再使用。

创建Observable

与Observer不同的是,Observable是通过 create() 方法来创建的。注意String是发送参数的类型:

//创建Observable
Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
 @Override
 public void call(Subscriber<? super String> subscriber) {
  subscriber.onNext("Hello");
  subscriber.onNext("World");
  subscriber.onCompleted();
 }
});

关于这其中的流程,我们暂且不考虑。

订阅(Subscribe)

在之前,我们创建了 Observable 和 Observer ,现在就需要用 subscribe() 方法来将它们连接起来,形成一种订阅关系:

//订阅
observable.subscribe(observer);

这里其实确实有点奇怪,为什么是Observable(被观察者)订阅了Observer(观察者)呢?其实我们想一想之前Button的点击事件:

Button.setOnClickListener(new View.OnClickListener())

Button是被观察者,OnClickListener是观察者,setOnClickListener是订阅。我们惊讶地发现,也是被观察者订阅了观察者,所以应该是一种流式API的设计吧,也没啥影响。

完整代码如下:

 //创建Observer
 Observer<String> observer = new Observer<String>() {
  @Override
  public void onNext(String s) {
   Log.i("onNext ---> ", "Item: " + s);
  }

  @Override
  public void onCompleted() {
   Log.i("onCompleted ---> ", "完成");
  }

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

 //创建Observable
 Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
  @Override
  public void call(Subscriber<? super String> subscriber) {
   subscriber.onNext("Hello");
   subscriber.onNext("World");
   subscriber.onCompleted();
  }
 });

 //订阅
 observable.subscribe(observer);

运行的结果如下,可以看到Observable中发送的String已经被Observer接收并打印了出来:

线程控制——Scheduler

好了,这里就是RxJava的精髓之一了。

在RxJava中,Scheduler相当于线程控制器,可以通过它来指定每一段代码运行的线程。

RxJava已经内置了几个Scheduler,下面是总结:

Schedulers.immediate() : 直接在当前线程运行,相当于不指定线程。这是默认的Scheduler。

Schedulers.newThread() : 总是启用新线程,并在新线程执行操作。

Schedulers.io() : I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的Scheduler。行为模式和newThread()差不多,区别在于io()的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下io()比newThread()更有效率。不要把计算工作放在io()中,可以避免创建不必要的线程。

Schedulers.computation() : 计算所使用的Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个Scheduler使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在computation()中,否则 I/O 操作的等待时间会浪费 CPU。

AndroidSchedulers.mainThread() ,Android专用线程,指定操作在主线程运行。

那我们如何切换线程呢?RxJava中提供了两个方法:subscribeOn() observeOn() ,两者的不同点在于:

subscribeOn() : 指定subscribe()订阅所发生的线程,即 call() 执行的线程。或者叫做事件产生的线程。

observeOn() : 指定Observer所运行在的线程,即onNext()执行的线程。或者叫做事件消费的线程。

具体实现如下:

//改变运行的线程
observable.subscribeOn(Schedulers.io());
observable.observeOn(AndroidSchedulers.mainThread());

这里确实不好理解,没关系,下面我们在具体例子中观察现象。

而这其中的原理,会在之后的源码级分析的文章中详细解释,现在我们暂且搁下。

第一个RxJava案例

好了,当看完之前的所有基础东西,现在我们就完全可以写一个基于RxJava的Demo了。

这里我们用一个基于RxJava的异步加载网络图片来演示。

由于重点在于RxJava对于异步的处理,所以关于如何通过网络请求获取图片,这里就不详细说明了。

另外这里采用的是链式调用,并为重要位置打上Log日志,观察方法执行的所在线程。

首先需要添加依赖,这没什么好说的:

dependencies {
 compile fileTree(include: ['*.jar'], dir: 'libs')
 testCompile 'junit:junit:4.12'
 ...
 compile 'io.reactivex:rxjava:1.1.6'

}

然后按照步骤来,首先通过create创建Observable,注意发送参数的类型是Bitmap:

//创建被观察者
Observable.create(new Observable.OnSubscribe<Bitmap>() {
 /**
 * 复写call方法
 *
 * @param subscriber 观察者对象
 */
 @Override
 public void call(Subscriber<? super Bitmap> subscriber) {
  //通过URL得到图片的Bitmap对象
  Bitmap bitmap = GetBitmapForURL.getBitmap(url);
  //回调观察者方法
  subscriber.onNext(bitmap);
  subscriber.onCompleted();
  Log.i(" call ---> ", "运行在 " + Thread.currentThread().getName() + " 线程");
 }
})

然后我们需要创建Observer,并进行订阅,这里是链式调用

.subscribe(new Observer<Bitmap>() { //订阅观察者(其实是观察者订阅被观察者)

 @Override
 public void onNext(Bitmap bitmap) {
  mainImageView.setImageBitmap(bitmap);
  Log.i(" onNext ---> ", "运行在 " + Thread.currentThread().getName() + " 线程");
 }

 @Override
 public void onCompleted() {
  mainProgressBar.setVisibility(View.GONE);
  Log.i(" onCompleted ---> ", "完成");
 }

 @Override
 public void onError(Throwable e) {
  Log.e(" onError --->", e.toString());
 }
 });

当然网络请求是耗时操作,我们需要在其他线程中执行,而更新UI需要在主线程中执行,所以需要设置线程:

.subscribeOn(Schedulers.io()) // 指定subscribe()发生在IO线程
.observeOn(AndroidSchedulers.mainThread()) // 指定Subscriber的回调发生在UI线程

这样我们就完成了一个RxJava的基本编写,现在整体看一下代码:

//创建被观察者
Observable.create(new Observable.OnSubscribe<Bitmap>() {
 /**
 * 复写call方法
 *
 * @param subscriber 观察者对象
 */
 @Override
 public void call(Subscriber<? super Bitmap> subscriber) {
  //通过URL得到图片的Bitmap对象
  Bitmap bitmap = GetBitmapForURL.getBitmap(url);
  //回调观察者方法
  subscriber.onNext(bitmap);
  subscriber.onCompleted();
  Log.i(" call ---> ", "运行在 " + Thread.currentThread().getName() + " 线程");
 }
})
.subscribeOn(Schedulers.io()) // 指定subscribe()发生在IO线程
.observeOn(AndroidSchedulers.mainThread()) // 指定Subscriber的回调发生在UI线程
.subscribe(new Observer<Bitmap>() { //订阅观察者(其实是观察者订阅被观察者)

 @Override
 public void onNext(Bitmap bitmap) {
  mainImageView.setImageBitmap(bitmap);
  Log.i(" onNext ---> ", "运行在 " + Thread.currentThread().getName() + " 线程");
 }

 @Override
 public void onCompleted() {
  mainProgressBar.setVisibility(View.GONE);
  Log.i(" onCompleted ---> ", "完成");
 }

 @Override
 public void onError(Throwable e) {
  Log.e(" onError --->", e.toString());
 }
 });

好了,下面是运行的动态图:


RxJava异步加载网络图片

现在来看一下运行的Log日志:


Log

可以看到,call方法(事件产生)执行在IO线程,而onNext方法(事件消费)执行在main线程。说明之前分析的是对的。

总结

好了,由于本文是一个RxJava的基础,所以篇幅稍微过长了点。即使这样,很多细节性问题都没有交代清楚。但所幸的是,本文已经将RxJava必要的基础入门知识讲解完了。可能由于技术水平有限,文中难免会有错误或者疏忽之处,欢迎大家指正与交流。希望这篇文章对大家的学习或者工作带来一定的帮助,小编还会陆续更新相关的文章,感兴趣的朋友们请继续关注我们。

(0)

相关推荐

  • Android性能优化之利用Rxlifecycle解决RxJava内存泄漏详解

    前言: 其实RxJava引起的内存泄漏是我无意中发现了,本来是想了解Retrofit与RxJava相结合中是如何通过适配器模式解决的,结果却发现了RxJava是会引起内存泄漏的,所有想着查找一下资料学习一下如何解决RxJava引起的内存泄漏,就查到了利用Rxlifecycle开源框架可以解决,今天周末就来学习一下如何使用Rxlifecycle. 引用泄漏的背景: RxJava作为一种响应式编程框架,是目前编程界网红,可谓是家喻户晓,其简洁的编码风格.易用易读的链式方法调用.强大的异步支持等使得R

  • Java的RxJava库操作符的用法及实例讲解

    操作符就是为了解决对Observable对象的变换的问题,操作符用于在Observable和最终的Subscriber之间修改Observable发出的事件.RxJava提供了很多很有用的操作符. 比如map操作符,就是用来把把一个事件转换为另一个事件的. Observable.just("Hello, world!") .map(new Func1<String, String>() { @Override public String call(String s) { r

  • RxJava入门指南及其在Android开发中的使用示例

    RxJava的GitHub主页,部署部分就没什么好说的了~ https://github.com/ReactiveX/RxJava 基础 RxJava最核心的两个东西是Observables(被观察者,事件源)和Subscribers(观察者).Observables发出一系列事件,Subscribers处理这些事件.这里的事件可以是任何你感兴趣的东西(触摸事件,web接口调用返回的数据...) 一个Observable可以发出零个或者多个事件,知道结束或者出错.每发出一个事件,就会调用它的Su

  • 关于RxJava的一些特殊用法小结

    本文主要给大家介绍了关于RxJava的一些特殊用法,分享出来供大家参考学习,需要的朋友们下面来一起看看吧. 一.按钮绑定 通过 RxView 可以对 view 进行快速的 clicks 绑定 RxView.clicks(button).debounce(300, TimeUnit.MILLISECONDS).subscribe(new Action1<Void>() { @Override public void call(Void aVoid) { Log.i("test"

  • Android中用RxJava和ViewPager实现轮播图

    前言 很多人要实现轮播图都会想到使用ViewPager + Handler来完成轮播图的效果.但是在RxJava快速发展的情况下,已经可以使用RxJava来代替Handler完成这样任务了. 下面我们就来介绍如何实现RxJava+ViewPager的轮播图. 效果图如下 ViewPager的操作 说到ViwePager应该大家都不陌生,它可以结合普通的View也可以结合Fragment一起使用.在此我也就不对它的使用方法进行过多的介绍了.直接开始介绍轮播的方法. 常见的轮播操作 private

  • android使用RxJava实现预加载

    在上一篇文章中介绍了使用非RxJava环境下,使用Handler机制SyncBarrier的特性实现预加载功能的方法. 在RxJava的环境下使用BehaviorSubject的特性来实现也是很方便的. BehaviorSubject内部会缓存消息流中最近的一个消息, 在后续有Subscriber订阅时,会直接将缓存的消息发送给Subscriber. RxPreLoader.java封装如下: import android.support.annotation.NonNull; import j

  • Java扩展库RxJava的基本结构与适用场景小结

    基本结构 我们先来看一段最基本的代码,分析这段代码在RxJava中是如何实现的. Observable.OnSubscribe<String> onSubscriber1 = new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("1"); subscrib

  • RxJava入门之介绍与基本运用

    前言 因为这个RxJava内容不算少,而且应用场景非常广,所以这个关于RxJava的文章我们会陆续更新,今天就来先来个入门RxJava吧 初识RxJava 什么是Rx 很多教程在讲解RxJava的时候,上来就介绍了什么是RxJava.这里我先说一下什么是Rx,Rx就是ReactiveX,官方定义是: Rx是一个函数库,让开发者可以利用可观察序列和LINQ风格查询操作符来编写异步和基于事件的程序 看到这个定义我只能呵呵,稍微通俗点说是这样的: Rx是微软.NET的一个响应式扩展.Rx借助可观测的序

  • 关于Redis数据库入门详细介绍

    目录 1.Redis是什么? 2.Redis特点: 单线程为何如此快? 3.redis 对比 memcached 4.redis 典型应用场景: 5.Redis下载与安装: Redis服务控制: Redis 命令工具: redis-benchmark 测试工具 redis-cli 命令行工具: 1.Redis是什么? 非关系型数据库:NoMsql 主流的 NoSQL 数据库有Redis. MongBD. Hbase. Memcached 等. Redis译为"远程字典服务",它是一款基

  • C# 语言入门基础介绍

    目录 一..NET 体系结构 二.Hello world 三.类型和变量 四.程序结构 前言: C#(读作"See Sharp")是一种新式编程语言,不仅面向对象,还类型安全. 开发人员利用 C# 能够生成在 .NET 中运行的多种安全可靠的应用程序. C# 源于 C 语言系列,C.C++.Java 和 JavaScript 程序员很快就可以上手使用. 本教程概述了 C# 8 及更高版本中该语言的主要组件. C# 是面向对象的.面向组件的编程语言. C# 提供了语言构造来直接支持这些概

  •  typeScript入门基础介绍

    目录 一.安装 TS 二.Vscode 自动编译 ts 三.入门 TS 基础数据类型 接口 类 TS 的特点: 始于 javaScript 归于 javaScript . 强大的类型系统. 先进的 javaScript . 适合开发大型项目,编译成纯 js 代码,js 可以运行在任何浏览器上. typeScript 是区分大小写的一门语言,本篇文章主要带你了解 ts 的安装,环境配置,以及简单的入门. 一.安装 TS 使用之前需要安装: npm install -g typescript 安装完

  • Mercurial入门学习介绍

    Mercurail是一种分步式的版本控制器,和git类似,但是和传统的svn有着很大的区别,可以参与这个讨论:Mercurial vs Subversion 安装 Ubuntu 1. 下载源代码[http://mercurial.selenic.com/wiki/Download#Source_packages] 2. 稳定版的下载网页[http://mercurial.selenic.com/release/?M=D] 3. 选择最新的版本:比如我是mercurial-2.4.tar.gz 4

  • SSB(SQLservice Service Broker) 入门实例介绍

    step 1:perpar database & data 复制代码 代码如下: use mastergoCreate database Inventorygouse InventorygoCreate table inventory(    TitleId nvarchar(6) not null,    Quantity int not null,    primary key (TitleId,Quantity));go insert inventory values ('pc1001',

  • java同步开篇入门简单介绍

    概览 这一篇的内容是一个大概的介绍,大致包含三大主题:java中的锁.同步器.分布式锁,大致讲的内容如下: (1)volatile (2)synchronized (3)AQS及Condition (4)ReentrantLock (5)ReentrantReadWriteLock (6)StampedLock (7)CountDownLatch (8)Semaphore (9)CyclicBarrier (10)Phaser (11)Mysql实现分布式锁 (12)Redis实现分布式锁 (1

  • Java中集合List、Set和Map的入门详细介绍

    目录 一.Collection接口 二.List集合 2.1介绍 2.1.1 ArrayList(数组) 2.1.2 Vector(数组实现.线程同步) 2.1.3 LinkList(链表) 2.2 List特性 2.3 List常用方法 2.4 List总结 三.Set集合 3.1介绍 3.2 分类 3.2.1 HashSet(Hash表) 3.2.2 TreeSet(二叉树) 3.2.3 LinkHashSet(HashSet+LinkedHashMap) 四.Map集合 4.1 HashM

  • JavaScript入门学习书籍推荐

    在 JavaScript 方面,自己不是什么专家,也不是什么高手,但自己一路走来,JavaScript 从迷茫到认识,对于 JavaScript 书籍的认识或许还有些借鉴价值. 入门推荐首选书籍:<JavaScript DOM 编程艺术 > 当初读了不下 4 遍,书内容简单,易学,上手快,编程思想严谨.好的入门书,对你未来的编程都会有着深远的影响.此本书不辜负这个历史使命. 更详细的评论可以看 Realazy 在豆瓣上的评论在 豆瓣 上的评论<通往终点的过程与终点本身同样重要> 基

随机推荐