RxJava-1 指南

23 篇文章 0 订阅
5 篇文章 0 订阅

本文基于扔物线的 给 Android 开发者的 RxJava 详解 提炼简化,感谢。

一、RxJava 是什么

一个词:异步。

说到根上,它就是一个实现异步操作的库,而别的定语都是基于这之上的。

二、RxJava 好在哪里

一个词:简洁。

能把什么复杂逻辑都能穿成一条线的简洁。

三、API 介绍和原理解析

3.1 概念:扩展的观察者模式

四个基本概念:Observable、Observer、subscribe、事件。

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

onCompleted():事件队列完结。当不再有 onNext() 发出时,需要触发 onCompleted() 作为结束。

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

在一个正确运行的事件序列中,onCompleted 和 onError 有且只有一个,并且是事件序列中的最后一个。它们是互斥的。

3.2 基本实现

3.2.1 创建 Observer

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

Observer<String> observer = new Observer<String>() {
	@Override
	public void onNext(String s){
	}
	@Override
	public void onCompleted(){
	}
	@Override
	public void onError(Throwable e){
	}
}

除了 Observer 接口之外,RxJava 还内置了一个实现了 Observer 的抽象类:Subscriber。Subscriber 对 Observer 接口进行了一些扩展,但它们的基本使用方式完全一样。

Subscriber<String> observer = new Subscriber<String>() {
	@Override
	public void onNext(String s){
	}
	@Override
	public void onCompleted(){
	}
	@Override
	public void onError(Throwable e){
	}
}

在 RxJava 的 subscribe 过程中,observer 会先被转换成一个 Subscriber 再使用。它们的区别主要有两点:

  1. onStart

这是 Subscriber 新增的方法。他会在 subscribe 刚开始,而事件还未发送之前被调用,可以用于做一些准备工作,例如数据的清零或重置。这是一个可选方法,默认实现为空。如果对准备工作的线程有要求(如弹 dialog,必须在主线程),onStart() 就不适用了,因为它总是在 subscribe 所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用 doOnSubScribe() 方法,后面会提到。

  1. unsubscribe

这是 Subscriber 实现的另一个接口 Subscription 的方法,用于取消订阅。在这个方法被调用后,Subscriber 将不再接受事件。一般在这个方法调用前,可以使用 isUnsubscribed() 先判断一下状态。unsubscribe() 这个方法很重要,因为在 subscribe() 之后,observable 会持有 Subscriber 的引用,如果不及时释放,可能会内存泄漏。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(onPause、onStop 等)调用 unsubscribe() 来解除引用关系,避免内存泄漏。

3.2.2 创建 Observable

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

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

Observable.OnSubscribe 即被观察的对象,上面是一个 String 对象。

call 方法里是订阅者订阅后的操作,在 subscribe 后会进行调用。

除了 create,RxJava 还提供了 just、from 等方法来简化操作:

just(T...): 将传入的参数依次发送出来。

Observable observable = Observable.just("Hello", "Hi", "Aloha");
// 将会依次调用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();

from(T[]) / from(Iterable<? extends T>): 将传入的数组或 Iterable 拆分成具体对象后,依次发送出来。

String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);
// 将会依次调用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();

3.2.3 Subscribe

创建了 Observable 和 Observer 之后,再用 subscribe() 方法将它们联结起来,整条链子就可以工作了。代码形式很简单:

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

Observable.subscribe(Subscriber) 的内部实现是这样的(仅核心代码):

// 注意:这不是 subscribe() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
// 如果需要看源码,可以去 RxJava 的 GitHub 仓库下载。
public Subscription subscribe(Subscriber subscriber) {
    subscriber.onStart();
    onSubscribe.call(subscriber);
    return subscriber;
}

可以看到,subscriber() 做了3件事:

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

3.2.4 场景示例

由指定的一个 drawable 文件 id drawableRes 取得图片,并显示在 ImageView 中。

Subscriber<Drawable> subscriber = new Subscriber<Drawable>() {
	@Override
	public void onNext(Drawable drawable){
		imageView.setImageDrawablw(drawable);
	}
	...
};

Observable observable = Observerable.create(new Observable.OnSubscribe<Drawable>(){
	@Override
	public void call(Subscriber<? super Drawable> subscriber) {
		Drawable drawable = getTheme().getDrawable(drawableRes));
		subscriber.onNext(drawable);
		subscriber.onCompleted();
	}
});

observable.subscribe(subscriber);

3.3 线程控制——Scheduler(一)

在不指定线程的情况下,RxJava 线程不变,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在那个生产事件,就在哪个线程消费事件。如果需要切换线程,就需要用到 Scheduler。

3.3.1 Scheduler 的 API(一)

在 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。
  • Andoid 中有一个专用的 AndroidSchedulers.mainThread(),它在主线程运行。

Scheduler 的调用:

  • subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。
  • observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。

如上面的取 Drawable 的例子:

Subscriber<Drawable> subscriber = new Subscriber<Drawable>() {
	@Override
	public void onNext(Drawable drawable){
		imageView.setImageDrawablw(drawable);
	}
	...
};

Observable observable = Observerable.create(new Observable.OnSubscribe<Drawable>(){
	@Override
	public void call(Subscriber<? super Drawable> subscriber) {
		Drawable drawable = getTheme().getDrawable(drawableRes));
		subscriber.onNext(drawable);
		subscriber.onCompleted();
	}
});

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

这样,加载图片会发生在 IO 线程,设置图片在主线程。即使加载图片耗费了几十展示几百毫秒的世界,也不会造成界面的卡顿。

3.4 变换

RxJava 提供了对事件序列进行变换的支持。所谓“变换”,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。

3.4.1 API

Obaservable.just("images/logo.png")
	.map(new Func1<String, Bitmap>() {
		@Override
		public Bitmap call(String filePath) {
			return getBitmapFromPath(filePath);
		}
	})
	.subscribe(new Action1<Bitmap>() {
		@Override
		public void call(Bitmap bitmap) {
			showBitmap(bitmap);
		}
	});

Func1 类是 RxJava 的一个接口,用于包装含有一个参数的方法。FuncX 和 ActionX 的区别在于,FuncX 包装的是有返回值的方法。

map() 方法将参数中的 String 对象转换从一个 Bitmap 对象后返回,而在经过 map 之后,事件的参数类型也由 String 转为了 Bitmap。这是最常见也最容易理解的变换,但 RxJava 的变换不止如此,它不仅可以针对事件对象,也可以针对整个事件序列。

flatMap

这是一个很有用但很难理解的变换。

如要打印一串 Student 的名字

Student[] students = ...;
Subscriber<String> subscriber = new Subscriber<String>() {
	@Override
	public void onNext(String name){
		Log.d(tag, name);
	}
	...
};

Observable.from(students)
	.map(new Func1<Student, String>() {
		@Override
		public String call(Student student){
			return student.getName();
		}
	})
	.subsrcibe(subscriber);

再假设:要打印出每个学生要修的所有课程的名字呢?

Student[] students = ...;
Subscriber<Student> subscriber = new Subscriber<Student>() {
	@Override
	public void onNext(Student student){
        List<Course> courses = student.getCourses();
        for (int i = 0; i < courses.size(); i++) {
            Course course = courses.get(i);
            Log.d(tag, course.getName());
        }
	}
	...
};

Observable.from(students)
	.subsrcibe(subscriber);

如果不想在 onNext 里使用 for 循环,而是直接处理 Course 对象呢?

Student[] students = ...;
Subscriber<Course> subscriber = new Subscriber<Course>() {
	@Override
	public void onNext(Course course){
		Log.d(tag, course.getName);
	}
};

Observable.from(students)
	.flatMap(new Func1<Student, Observable<Course>>(){
		@Override
		public Observable<Course> call(Student student){
			return Observable.from(student.getCourses());
		}
	})
	.subscribe(subscriber);
map 和 flatMap 的区别
Observable.from(students)
    .map(new Func1<Student, Observable<Course>>(){
        @Override
        public Observable<Course> call(Student student){
            return Observable.from(student.getCourses());
        }
    })
    .subscribe(subscriber);
    
    
Observable.from(students)
    .flatMap(new Func1<Student, Observable<Course>>(){
        @Override
        public Observable<Course> call(Student student){
            return Observable.from(student.getCourses());
        }
    })
    .subscribe(subscriber);

map

通过 Student–>Observable<Course> 的 map,将 Student 序列变成 Observable<Course> 序列。

flatMap

通过 Student–>Observable<Course> 的 map,将 Student 序列变成 Observable<Course> 序列。

再通过 flat(铺平),将 Observable<Course> 序列变成 course 序列。

所有其实应该叫 mapFlat。

3.4.2 变换的原理 lift()

变换的功能虽然各有不同,但实质上都是针对事件序列的处理和再发送。在 RxJava 内部,他们基于同一个基础的变换方法:lift(Operator)。首先看一下 lift() 的内部实现(仅核心代码):

// 注意:这不是 lift() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
// 如果需要看源码,可以去 RxJava 的 GitHub 仓库下载。
public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
    return Observable.create(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber subscriber) {
            Subscriber newSubscriber = operator.call(subscriber);
            newSubscriber.onStart();
            onSubscribe.call(newSubscriber);
        }
    });
}

在 Observable 执行了 lift(Operator) 方法之后,会返回一个新的 Observable,这个新的 Observable 会像一个代理一样,负责接收原始的 Observable 发出的事件,并在处理后发送给 Subscriber。

变换的具体效果通过 Operator 实现,Operator 的 call 方法接受一个 Subscriber 参数,返回一个新的 Subscriber,这个新的 Subscriber 订阅了原始的 Observable,并在原始的 Observable 变化时调用旧的 Subscriber 的处理方法。

总结:

要让 Subscriber<B> 能够订阅 Observable<A>,就要通过 Observable<A> 产生一个 Observable<B>,同时 Observable<B> 能够订阅 Observable<A>,Observable<B> 里就需要一个 Subscriber<A>。

Operator 做的就是将 Subscriber<B> 包装为 Subscriber<A>,通过 Subscriber<A> 可以调用 Subscriber<B>。

即使不用变换,Subscriber<A> 直接订阅 Observable<A>,然后再做 A-B 的变换也是可以的,就像上面取课程名的 for 循环,变换的好处就在于可以将一系列操作转为链式,逻辑更加清晰。

sss

3.4.3 compose:对 Observable 整体的变换

除了 lift() 之外, Observable 还有一个变换方法叫做 compose(Transformer)。它和 lift() 的区别在于, lift() 是针对事件项和事件序列的,而 compose() 是针对 Observable 自身进行变换。举个例子,假设在程序中有多个 Observable ,并且他们都需要应用一组相同的 lift() 变换。你可以这么写:

public class LiftAllTransformer implements Observable.Transformer<Integer, String> {
    @Override
    public Observable<String> call(Observable<Integer> observable) {
        return observable
            .lift1()
            .lift2()
            .lift3()
            .lift4();
    }
}
...
Transformer liftAll = new LiftAllTransformer();
observable1.compose(liftAll).subscribe(subscriber1);
observable2.compose(liftAll).subscribe(subscriber2);
observable3.compose(liftAll).subscribe(subscriber3);
observable4.compose(liftAll).subscribe(subscriber4);

使用 compose() 方法,Observable 可以利用传入的 Transformer 对象的 call 方法直接对自身进行处理,不必被包在方法的里面了。

3.5 线程控制——Scheduler(二)

3.5.1 Scheduler 的 API(二)

可以利用 subscribeOn() 结合 observeOn() 来实现线程控制,让事件的产生和消费发生在不同的线程。

observeOn() 指定的是 Subscriber 的线程,而这个 Subscriber 是 observeOn() 执行时的当前 Observable 所对应的 Subscriber ,即它的直接下级 Subscriber 。换句话说,observeOn() 指定的是它之后的操作所在的线程。因此如果有多次切换线程的需求,只要在每个想要切换线程的位置调用一次 observeOn() 即可。

Observable.just(1, 2, 3, 4) // IO 线程,由 subscribeOn() 指定
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.newThread())
    .map(mapOperator) // 新线程,由 observeOn() 指定
    .observeOn(Schedulers.io())
    .map(mapOperator2) // IO 线程,由 observeOn() 指定
    .observeOn(AndroidSchedulers.mainThread) 
    .subscribe(subscriber);  // Android 主线程,由 observeOn() 指定

如上,通过 observeOn() 的多次调用,程序实现了线程的多次切换。

不过,不同于 observeOn() , subscribeOn() 的位置放在哪里都可以,但它是只能调用一次的。

3.5.2 Scheduler 的原理(二)

其实, subscribeOn() 和 observeOn() 的内部实现,也是用的 lift()。

subscribeOn() 和 observeOn() 都做了线程切换的工作。不同的是, subscribeOn() 的线程切换发生在 OnSubscribe 中,即在它通知上一级 OnSubscribe 时,这时事件还没有开始发送,因此 subscribeOn() 的线程控制可以从事件发出的开端就造成影响;而 observeOn() 的线程切换则发生在它内建的 Subscriber 中,即发生在它即将给下一级 Subscriber 发送事件时,因此 observeOn() 控制的是它后面的线程。

3.5.3 延伸:doOnSubscribe()

然而,虽然超过一个的 subscribeOn() 对事件处理的流程没有影响,但在流程之前却是可以利用的。

在前面讲 Subscriber 的时候,提到过 Subscriber 的 onStart() 可以用作流程开始前的初始化。然而 onStart() 由于在 subscribe() 发生时就被调用了,因此不能指定线程,而是只能执行在 subscribe() 被调用时的线程。这就导致如果 onStart() 中含有对线程有要求的代码(例如在界面上显示一个 ProgressBar,这必须在主线程执行),将会有线程非法的风险,因为有时你无法预测 subscribe() 将会在什么线程执行。

而与 Subscriber.onStart() 相对应的,有一个方法 Observable.doOnSubscribe() 。它和 Subscriber.onStart() 同样是在 subscribe() 调用后而且在事件发送前执行,但区别在于它可以指定线程。默认情况下, doOnSubscribe() 执行在 subscribe() 发生的线程;而如果在 doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。

示例代码:

Observable.create(onSubscribe)
    .subscribeOn(Schedulers.io())
    .doOnSubscribe(new Action0() {
        @Override
        public void call() {
            progressBar.setVisibility(View.VISIBLE); // 需要在主线程执行
        }
    })
    .subscribeOn(AndroidSchedulers.mainThread()) // 指定主线程
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(subscriber);

如上,在 doOnSubscribe()的后面跟一个 subscribeOn() ,就能指定准备工作的线程了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值