关于RxJava2.0的文章,各大论坛上已经有很多大神写的很详细了。
作为一个开发新人,在大佬面前只有递茶的份的渣渣。对Rxjava原理只有肤浅的理解,所以本篇只是简单介绍和记录一下Rxjava的使用,一些定义解析都是挪用大神的文字,请见谅。
关于深入原理,同学可以找度娘观摩大神的文章。
-------------------------------------------------------------------------------------------------
1. RxJava到底是什么:
• RxJava 在 GitHub 主页上的自我介绍是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库)。这就是 RxJava ,概括得非常精准。
• 其实, RxJava 的本质可以压缩为异步这一个词。说到根上,它就是一个实现异步操作的库,而别的定语都是基于这之上的。
2. RxJava 好在哪
• 同样是做异步,为什么人们用它,而不用现成的 AsyncTask / Handler / XXX / ... ?』
• 异步操作很关键的一点是程序的简洁性,因为在调度过程比较复杂的情况下,异步代码经常会既难写也难被读懂。 Android 创造的 AsyncTask 和Handler ,其实都是为了让异步代码更加简洁。RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁。
• RxJava 的实现,是一条从上到下的链式调用,没有任何嵌套,这在逻辑的简洁性上是具有优势的。当需求变得复杂时,这种优势将更加明显
• 所以, RxJava 好在哪?就好在简洁,好在那把什么复杂逻辑都能穿成一条线的简洁
3. 概念:扩展的观察者模式
RxJava 的异步实现,是通过一种扩展的观察者模式来实现的
观察者模式面向的需求是:A 对象(观察者)对 B 对象(被观察者)的某种变化高度敏感,需要在 B 变化的一瞬间做出反应。观察者不需要时刻盯着被观察者,而是采用注册(Register)或者称为订阅(Subscribe)的方式,告诉被观察者:我需要你的某某状态,你要在它变化的时候通知我。采取这样被动的观察方式,既省去了反复检索状态的资源消耗,也能够得到最高的反馈速度。
RxJava 有四个基本概念:Observable (可观察者,即被观察者)、 Observer (观察者)、 subscribe (订阅)、事件。
• Observable 和 Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer。
• 与传统观察者模式不同, RxJava 的事件回调方法除了普通事件 onNext() 之外,还定义了两个特殊的事件:onCompleted() 和 onError()。
• onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志。
• onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。
• 在一个正确运行的事件序列中, onCompleted() 和 onError() 有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted() 和 onError() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。
4. 基本实现:
基于以上的概念, RxJava 的基本实现主要有三点:
• 创建 Observer
Observer 即观察者,它决定事件触发的时候将有怎样的行为。 RxJava 中的 Observer 接口的实现方式:
Observer<Integer> mObserver = new Observer<Integer>() {
private Disposable mDisposable;
private int index = 0;
@Override
public void onSubscribe(Disposable d) {
//Disposable用于解除订阅关系
mDisposable = d;
}
@Override
public void onNext(Integer integer) {
/* 对Observable传入的数据进行处理
* 在onNext执行mDisposable.dispose()来解除订阅关系
* 解除后则不再接收Observable发射来的数据
*/
index++;
if (integer == 2) {
mDisposable.dispose();
}
Log.d(TAG, "OnNext Value -> " + index);
}
@Override
public void onError(Throwable e) {
//处理错误信息
}
@Override
public void onComplete() {
//进行Observable发射数据完成后的操作
}
};
• 创建 Observable
Observable 即被观察者,它决定什么时候触发事件以及触发怎样的事件。 RxJava 使用 create() 方法来创建一个 Observable ,并为它定义事件触发规则:
Observable<Integer> mObservable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "Emitter Value -> " + 1);
emitter.onNext(1);
Log.d(TAG, "Emitter Value -> " + 2);
emitter.onNext(2);
Log.d(TAG, "Emitter Value -> " + 3);
emitter.onNext(3);
emitter.onComplete();
Log.d(TAG, "Emitter Value -> " + 4);
emitter.onNext(4);
Log.d(TAG, "Emitter Value -> " + 5);
emitter.onNext(5);
Log.d(TAG, "Emitter Value -> " + 6);
emitter.onNext(6);
}
});
•Subscribe (订阅)
创建了 Observable 和 Observer 之后,用 subscribe() 方法将它们联结起来,整条链子就可以工作了。
mObservable.subscribe(mObserver);
运行一下程序,结果:
com.example.ram D/RxjavaTest: Emitter Value -> 1
com.example.ram D/RxjavaTest: OnNext Value -> 1
com.example.ram D/RxjavaTest: Emitter Value -> 2
com.example.ram D/RxjavaTest: OnNext Value -> 2
com.example.ram D/RxjavaTest: Emitter Value -> 3
com.example.ram D/RxjavaTest: Emitter Value -> 4
com.example.ram D/RxjavaTest: Emitter Value -> 5
com.example.ram D/RxjavaTest: Emitter Value -> 6
针对输出结果,可以看到在执行disposable.dispose()之后,Observer就没有再接收Observable的信息了。
但仔细看的同学可以发现,在Observable发送emitter.onCompelte()之后,observer的OnComplete()并没有调用。
原因也跟上面一样,解除订阅关系之后,Observable可以继续传递状态参数,但Observer不再执行对应操作。
5. Rxjava中的操作符
• 转换操作符:RxJava 提供了对事件序列进行变换的支持,这是它的核心功能之一。
所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。
Map :将原有Observable通过某种的关系转换成另一个Observable
Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
for (int i = 0; i < 3; i++) {
emitter.onNext(i);
}
emitter.onComplete();
}
}).map(new Function<Integer, String>() {
@Override
public String apply(Integer s) throws Exception {
return s.toString() + "00";
}
}).subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String integer) {
Log.e(TAG, integer);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
Log.e(TAG, "Complete");
}
});
运行结果:Integer类型转换成String之后发射给Observer进行处理
com.example.ram E/RxjavaTest: 000
com.example.ram E/RxjavaTest: 100
com.example.ram E/RxjavaTest: 200
com.example.ram E/RxjavaTest: Complete
flatMap:它可以把一个发射器 Observable 通过某种方法转换为多个 Observables,然后再把这些分散的 Observables装进一个单一的发射器 Observable。但需要注意的是,flatMap 并不能保证事件的顺序,如果需要保证,需要用到 ConcatMap。
for (int j = 0; j < 10; j++) {
List<String> mStringList = new ArrayList<>();
for (int i = j * 10; i < j * 10 + 10; i++) {
mStringList.add(i % 10, "the number -> " + i);
}
mLists.add(mStringList);
}
Observable.fromIterable(mLists)
.flatMap(new Function<List<String>, ObservableSource<String>>() {
@Override
public ObservableSource<String> apply(List<String> strings) throws Exception {
int delay = 0;
if (strings.get(2).contains("2")) {
delay = 500;
}
return Observable.fromIterable(strings).delay(delay, TimeUnit.MILLISECONDS);
}
})
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
Log.e(TAG, s);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
运行结果:
com.example.ram E/RxjavaTest: the number -> 10
com.example.ram E/RxjavaTest: the number -> 11
com.example.ram E/RxjavaTest: the number -> 20
com.example.ram E/RxjavaTest: the number -> 12
com.example.ram E/RxjavaTest: the number -> 13
concatMap:可以做到不交错的发射两个甚至多个 Observable 的发射事件,并且只有前一个 Observable 终止(onComplete) 后才会订阅下一个 Observable。与flatMap的原理相同,唯一区别就是concatMap可以保证输出顺序,而flatMap不可以。
6. Map变换的原理:
举个栗子:Map的源码分析
@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) {
ObjectHelper.requireNonNull(mapper, "mapper is null");
return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper));
}
可以看到map方法的参数Function类有两个泛型参数,第一个数转换前的T类型,第二个是转换后的R类型
且函数需要返回的是一个R类型Observable
函数最终返回的是ObservableMap<T,R>类的对象,ObservableMap的部分源码:
final Function<? super T, ? extends U> function;
public ObservableMap(ObservableSource<T> source, Function<? super T, ? extends U> function) {
super(source);
this.function = function;
}
@Override
public void subscribeActual(Observer<? super U> t) {
source.subscribe(new MapObserver<T, U>(t, function));
}
subscribeActual 是个重要的钩子方法,在该方法传入一个U类型(也就是R类型)的Observer对象,
在方法里调用subscribe方法,订阅一个MapObserver类的对象。
在MapObserver类中,通过apply方法(在代码中重写的方法)返回U类型参数,然后把R类型的参数通过onNext接口传到Observer的onNext方法。以下是MapObserver的onNext方法源码:
@Override
public void onNext(T t) {
if (done) {
return;
}
if (sourceMode != NONE) {
downstream.onNext(null);
return;
}
U v;
try {
v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");
} catch (Throwable ex) {
fail(ex);
return;
}
downstream.onNext(v);
}
所以Map的内部转换原理:将原来T类型的Observable重新订阅一个R类型的Observer,把代码中返回的R值,通过OnNext传到R类型的Observer中。
7. 线程控制 -- Scheduler
在不指定线程的情况下, RxJava 遵循的是线程不变的原则,即:在哪个线程调用 subscribe(),就在哪个线程生产事件;在哪个线程生产事件,就在哪个线程消费事件。如果需要切换线程,就需要用到 Scheduler (调度器)
在RxJava 中,Scheduler ——调度器,相当于线程控制器,RxJava 通过它来指定每一段代码应该运行在什么样的线程。RxJava 已经内置了几个 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。
• Android 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。
代码演示:
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
for (int i = 0; i < 5; i++) {
Log.e(TAG, "number -> " + i + ", Thread -> " + Thread.currentThread().getName());
emitter.onNext( String.valueOf(i) );
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(String s) {
Log.e(TAG, "Str ->" + s + ", Thread -> " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
这段代码中,由于 subscribeOn(Schedulers.io()) 的指定,被创建的事件的内容 0、1、2、3、4 将会在 IO 线程发出;
由于 observeOn(AndroidScheculers.mainThread()) 的指定,因此 subscriber 数字的打印将发生在主线程 。
这种在 subscribe() 之前写上两句 subscribeOn(Scheduler.io()) 和 observeOn(AndroidSchedulers.mainThread()) 的使用方式很常见,适用于多数的 『后台线程取数据,主线程显示』的程序策略。
运行结果:
com.example.ram E/RxjavaTest: number -> 0, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: number -> 1, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: number -> 2, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: number -> 3, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: number -> 4, Thread -> RxCachedThreadScheduler-1
com.example.ram E/RxjavaTest: Str ->0, Thread -> main
com.example.ram E/RxjavaTest: Str ->1, Thread -> main
com.example.ram E/RxjavaTest: Str ->2, Thread -> main
com.example.ram E/RxjavaTest: Str ->3, Thread -> main
com.example.ram E/RxjavaTest: Str ->4, Thread -> main
8. 今天总结了一下Observable的简单使用、Map/flatMap/concatMap等基本转换符、非常简单的介绍一下Scheduler
所以贴下具体实际应用的代码,让同学们有更深的理解:
功能描述:根据图库返回的多张图片url,复制生成一张缩略图片。对缩略图进行旋转角处理,完成后返回新图片的绝对路径
代码:
List<Uri> uriList = data.getParcelableArrayListExtra("selectedItems");
Observable
.fromIterable(uriList)
.flatMap(new Function<Uri, ObservableSource<Uri>>() {
@Override
public ObservableSource<Uri> apply(Uri uri) throws Exception {
return Observable.just(uri);
}
})
.map(new Function<Uri, String>() {
@Override
public String apply(Uri uri) throws Exception {
/*
* 具体的骚操作可以围观文章“如何避免Image上传到服务器后发生转角度Rotation错误” ^ ^
*/
String pathStr = FileUtil.getImageAttrFromUri(mContext, FileUtil.PATH, uri);
String fileExtension = FileUtil.getFileExtension(pathStr);
String newFilePath = appDirtPath + File.separator + System.currentTimeMillis() + "." + fileExtension;
File mFile = BitmapUtil.correctImageRotateWithoutExif(pathStr, newFilePath, fileExtension, true);
if (mFile != null && mFile.exists()) {
appendFile(newFilePath);
return newFilePath;
} else {
return pathStr;
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
private int time = 0;
@Override
public void accept(String s) throws Exception {
time++;
//当返回filePath数据次数>=list的个数代表图片已经全部处理完
if (time >= list.size()) {
Toast.makeText(mContext, "图片加载完成", Toast.LENGTH_SHORT).show();
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(mContext, "图片加载失败", Toast.LENGTH_SHORT).show();
throwable.printStackTrace();
}
});
步骤分析:
• 通过fromIterable()创建一个Observable,并接收数据UriList
• 通过flatMap将UriList中每个uri通过Observable.just()发送到Observer
• 通过map将Uri经特定函数转换成String类型数据
• 因为在map进行数据转换耗时较多,所以通过subscribeOn()把当前线程切换为io()线程。
• 接收到处理后的数据,需要在UI上显示信息。所以通过observerOn()把之前切换io线程再切换为Main线程(UI线程)
• 最后根据返回的最终数据,在subscribe()的Consumer中进行处理。
注意:1. flatMap会对传入的listObservable创建多个list子项的Observable,需要注意的是:当list的子项还需分解子项时,return的是Observable.fromIterable(子项)
2.此时return的Observable并不会马上发送给subscribe()处 理,而是把子项的Observable汇入到同一个Observable,这个Observable再把事件统一发送给subscribe()处理。
----------------------------------------------------------------------------------------------------------------------------------------------------------------
记录一下RxJava2.0的使用方法,加深自己的理解。
文中若有错误地方,欢迎各位指出。