清晰易懂的RxJava入门实践
导入
我相信大家肯定对ReactiveX 和 RxJava 都不陌生,因为现在只要是和技术相关的网站,博客都会随处见到介绍ReactiveX和RxJava的文章。
ReactiveX
- ReactiveX是Reactive Extensions 的缩写,即响应式编程的扩展。
- “a library for composing asynchronous and event-based programs using observable sequences for the Java VM”(一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序库)。
- Rx是一种编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步 数据流.由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源。他在各个常用编程语言上都有实现。如Java,C#,PHP,Swift,Scala等等.社区网站是reactivex.io。
- ReactiveX不仅仅是一个编程接口,它是一种编程思想的突破,它影响了许多其它的程序库和框架以及编程语言。
关于响应式编程
事件总线(Event buses)或咱们常见的单击事件就是一个异步事件流,你可以观察这个流,也可以基于这个流做一些自定义操作。响应式就是基于这种想法。你能够创建所有事物的数据流,而不仅仅只是单击和悬停事件数据流。 流廉价且无处不在,任何事物都可以当作一个流:变量、用户输入、属性、缓存、数据结构等等。比如,假设你的微博评论就是一个跟单击事件一样的数据流,你能够监听这个流,并做出响应。
有一堆的函数能够创建(create)任何流,也能将任何流进行组合(combine)和过滤(filter)。 这正是“函数式”的魔力所在。一个流能作为另一个流的输入(input),甚至多个流也可以作为其它流的输入。你能合并(merge)两个流。你还能通过过滤(filter)一个流得到那些你感兴趣的事件。你能将一个流中的数据映射(map)到一个新的流中。
响应式编程的主要组成部分是observable, operator 和 observer
一般响应式编程的信息流:Observable -> Operator1 -> Operator2->...->OperatorN->Observer
Observable 是事件的生产者,Observer是事件的最终消费者。中间可以通过任意多个的Operator对事件进行处理和转换
因为Observer通常在主线程中执行,因此设计上要求代码尽可能的简单,只对事件作出相应(不对事件或者数据进行修改,所有修改事件的工作全部由operator完成)
RxJava 和 RxAndroid
RxJava是ReactiveX在 Java 平台的实现,你可以将它看作一个普通的Java类库。
RxAndroid是RxJava的一个针对Android平台的扩展,主要用于 Android 开发。
RxJava就是一个做异步开发的框架,和Android系统提供的 Handler+Thread,AsyncTask,Context.runOnUiThread等是解决的是同样的问题。那么他跟系统提供的异步编程方案比,有什么好处呢。或者说他有什么样的优势值得我们花时间和精力切换到RxJava呢?
总结起来可以用两个词来概括:异步和简洁
主要概念
Observable(被观察者)
Observables 负责发出一系列的事件,这里的事件可以是任何东西,例如网络请求的结果,复杂计算处理的结果,数据库操作的结构,文件操作的结果等,事件执行结束后交给Observer的回调处理。
Observer(观察者)
进行订阅接受处理事件
Operator(操作符)中文文档
负责对事件进行各种变化和处理
Scheduler(调度器)
提供了各种调度器,是RxJava可以方便的实现异步开发
事件
这里的事件值指的是 onNext (有新数据),onComplete (所有数据处理完成),onError (事件队列异常)
RxJava的好处(为什么RxJava对于Android如此重要)
轻松使用并发:让异步编程变得简单简洁.像写同步代码一样。
方便的线程切换
简单而完善的异常处理机制:传统的try/cache没办法处理异步中子线程产生的异常,RxJava 提供了合适的错误处理机制
强大的操作符支持,函数式的风格,链式调用。
举个例子
假如现在我们有这样一个需求:界面上有一个自定义的视图 imageCollectorView ,它的作用是显示多张图片,并能使用 addImage(Bitmap) 方法来任意增加显示的图片。现在需要程序将一个给出的目录数组 File[] folders 中每个目录下的 png 图片都加载出来并显示在 imageCollectorView 中。需要注意的是,由于读取图片的这一过程较为耗时,需要放在后台执行,而图片的显示则必须在 UI 线程执行。
目录数组
File[] folders=new File[]{......};复制代码
线程方式实现(call hell)
new Thread() {
@Override
public void run() {
super.run();
try{
for (File folder : folders) {
File[] files = folder.listFiles();
for (File file : files) {
if (file.getName().endsWith(".png")) {
final Bitmap bitmap = getBitmapFromFile(file);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
imageCollectorView.addImage(bitmap);
}
});
}
}
}
}catch(Exception e){
//error handling
//只能在这里进行异常处理
}
}
}.start();复制代码
RxJava 实现
//创建Observable
Observable observable = Observable.create(new ObservableOnSubscribe<File>() {
@Override
public void subscribe(ObservableEmitter<File> e) throws Exception {
for (File file : files) {
e.onNext(file);
}
e.onComplete();
}
});
//创建Observer
Observer<Bitmap> observer = new Observer<Bitmap>() {
@Override
public void onSubscribe(Disposable disposable) {
}
@Override
public void onNext(Bitmap bitmap) {
imageCollectorView.addImage(bitmap);
}
@Override
public void onError(Throwable throwable) {
//error handling
}
@Override
public void onComplete() {
Log.i(TAG,"All images are shown");
}
};
//对事件集进行处理并连接消费者
observable.flatMap(new Func1<File, Observable<File>>() {//分别获取每个文件夹下面的文件,组合成一个Observable
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {//过滤出所有扩展名为png的文件
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {//根据File对象,获取Bitmap对象
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())//指定Observable的所有操作符的操作在io线程中执行
.observeOn(AndroidSchedulers.mainThread())//指定消费者在主线程中执行
.subscribe(observer);//连接观察者
有的人可能说了,你这不是代码更多,更复杂了吗?
不要着急,这只是最基础的版本,稍后会对代码进行简化。
但即使是这种情况下,代码虽然多了,但我们可以发现,他的逻辑更清晰了,也没有那么多的嵌套了。
简化代码
- 对于一个数组,可用创建操作符“from”来创建Observable
- 如果我们只对结果感兴趣,不关心异常处理和事件发射完成事件,我也可以将Observer用Consumer来替换
//创建Observable
Observable observable = Observable.from(folers);
//创建Observer
Consumer<Bitmap> consumer=new Consumer<Bitmap>() {
@Override
public void accept(@NonNull Bitmap bitmap) throws Exception {
imageCollectorView.addImage(bitmap);
}
};
//对事件集进行处理并连接消费者
observable.flatMap(new Func1<File, Observable<File>>() {//分别获取每个文件夹下面的文件,组合成一个Observable
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {//过滤出所有扩展名为png的文件
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {//根据File对象,获取Bitmap对象
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())//指定Observable的所有操作符的操作在io线程中执行
.observeOn(AndroidSchedulers.mainThread())//指定消费者在主线程中执行
.subscribe(consumer);//连接消费者
RxJava 链式调用实现
Observable.from(folders)
.flatMap(new Func1<File, Observable<File>>() {
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Bitmap>() {
@Override
public void accept(@NonNull Bitmap bitmap) throws Exception {
imageCollectorView.addImage(bitmap);
}
});
RxJava + lambda 实现
Observable.from(folders)
.flatMap(file -> Observable.from(file.listFiles())
.filter(file -> file.getName().endsWith(".png"))
.map( file -> getBitmapFromFile(file))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> imageCollectorView.addImage(bitmap));//无异常处理,有异常会抛到主线程,不影响我们原来程序的crash处理
关于lambda(匿名函数,它可以包含表达式和语句)在Android中的使用:
- 要在 Android 的较早版本中测试 Lambda 表达式、方法引用和类型注解,请前往您的 build.gradle 文件,将 compileSdkVersion 和 targetSdkVersion 设置为 23 或更低。您仍需要启用 Jack 工具链以使用这些 Java 8 功能。
- 经测试,按照官方提供的方案配置后,虽然可以使用lambda,但编译速度变的很慢。
- 在运行的时候,并且我测试用的项目引用改了 Bouncy Castle(轻量级加密解密工具包) 这个包报出了内存溢出的异常,所以我感觉现在还不太稳定。
- 第三方开源的实现方案:retrolambda
- 当然我们也可以不用lambda,这样代码看着比较多,但因其只有一层嵌套的链式调用,所以逻辑结构并不复杂。事实上 Android Studio 会自动帮我们把这部分代码折叠成lambda的形式。
更进一步,假设我们现在需要忽略掉前5张,一共显示10张
Observable.from(folders)
.flatMap(file -> Observable.from(file.listFiles())
.filter(file -> file.getName().endsWith(".png"))
.skip(5)
.take(10)
.map( file -> getBitmapFromFile(file))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> imageCollectorView.addImage(bitmap));//无异常处理,有异常会抛到主线程,不影响我们原来程序的crash处理
操作符简介
创建操作
用于创建Observable的操作符
Create
— 通过调用观察者的方法从头创建一个Observablecreate操作符是所有创建型操作符的“根”,也就是说其他创建型操作符最后都是通过create操作符来创建Observable的
From
— 将其它的对象或数据结构转换为ObservableJust
— 将对象或者对象集合转换为一个会发射这些对象的ObservableDefer
— 在观察者订阅之前不创建这个Observable,为每一个观察者创建一个新的ObservableEmpty/Never/Throw
— 创建行为受限的特殊Observable一般用于测试
Interval
— 创建一个定时发射整数序列的ObservableRange
— 创建发射指定范围的整数序列的ObservableRepeat
— 创建重复发射特定的数据或数据序列的ObservableStart
— 创建发射一个函数的返回值的ObservableTimer
— 创建在一个指定的延迟之后发射单个数据的Observable
变换操作
这些操作符可用于对Observable发射的数据进行变换
Map
— 映射,通过对序列的每一项都应用一个函数变换Observable发射的数据,实质是对序列中的每一项执行一个函数,函数的参数就是这个数据项Buffer
— 缓存,可以简单的理解为缓存,它定期从Observable收集数据到一个集合,然后把这些数据集合打包发射,而不是一次发射一个FlatMap
— 扁平映射,将Observable发射的数据变换为Observables集合,然后将这些Observable发射的数据平坦化的放进一个单独的Observable,可以认为是一个将嵌套的数据结构展开的过程。GroupBy
— 分组,将原来的Observable分拆为Observable集合,将原始Observable发射的数据按Key分组,每一个Observable发射一组不同的数据Scan
— 扫描,对Observable发射的每一项数据应用一个函数,然后按顺序依次发射这些值Window
— 窗口,定期将来自Observable的数据分拆成一些Observable窗口,然后发射这些窗口,而不是每次发射一项。类似于Buffer,但Buffer发射的是数据,Window发射的是Observable,每一个Observable发射原始Observable的数据的一个子集
过滤操作
这些操作符用于从Observable发射的数据中进行选择,符合一定条件的发送给观察者进行处理,不符合条件的直接丢弃
Filter
— 过滤,过滤掉没有通过谓词测试的数据项,只发射通过测试的Skip
— 跳过前面的若干项数据SkipLast
— 跳过后面的若干项数据Take
— 只保留前面的若干项数据TakeLast
— 只保留后面的若干项数据Debounce
— 只有在空闲了一段时间后才发射数据,通俗的说,就是如果一段时间没有操作,就执行一次操作Distinct
— 去重,过滤掉重复数据项ElementAt
— 取值,取特定位置的数据项First
— 首项,只发射满足条件的第一条数据IgnoreElements
— 忽略所有的数据,只保留/终止通知(onError或onCompleted)Last
— 末项,只发射最后一条数据Sample
— 取样,定期发射最新的数据,等于是数据抽样,有的实现里叫ThrottleFirst
组合操作
组合操作符用于将多个Observable组合成一个单一的Observable
And/Then/When
— 通过模式(And条件)和计划(Then次序)组合两个或多个Observable发射的数据集CombineLatest
— 当两个Observables中的任何一个发射了一个数据时,通过一个指定的函数组合每个Observable发射的最新数据(一共两个数据),然后发射这个函数的结果Join
— 无论何时,如果一个Observable发射了一个数据项,只要在另一个Observable发射的数据项定义的时间窗口内,就将两个Observable发射的数据合并发射Merge
— 将两个Observable发射的数据组合并成一个StartWith
— 在发射原来的Observable的数据序列之前,先发射一个指定的数据序列或数据项Switch
— 将一个发射Observable序列的Observable转换为这样一个Observable:它逐个发射那些Observable最近发射的数据Zip
— 打包,使用一个指定的函数将多个Observable发射的数据组合在一起,然后将这个函数的结果作为单项数据发射
错误处理
这些操作符用于从错误通知中恢复
辅助操作
一组用于处理Observable的操作符
Delay
— 延迟一段时间发射结果数据Do
— 注册一个动作占用一些Observable的生命周期事件,相当于Mock某个操作Materialize/Dematerialize
— 将发射的数据和通知都当做数据发射,或者反过来ObserveOn
— 指定观察者观察Observable的调度程序(工作线程)SubscribeOn
— 指定Observable应该在哪个调度程序上执行Serialize
— 强制Observable按次序发射数据并且功能是有效的Subscribe
— 收到Observable发射的数据和通知后执行的操作TimeInterval
— 将一个Observable转换为发射两个数据之间所耗费时间的ObservableTimeout
— 添加超时机制,如果过了指定的一段时间没有发射数据,就发射一个错误通知Timestamp
— 给Observable发射的每个数据项添加一个时间戳Using
— 创建一个只在Observable的生命周期内存在的一次性资源
条件和布尔操作
这些操作符可用于单个或多个数据项,也可用于Observable
All
— 判断Observable发射的所有的数据项是否都满足某个条件Amb
— 给定多个Observable,只让第一个发射数据的Observable发射全部数据Contains
— 判断Observable是否会发射一个指定的数据项DefaultIfEmpty
— 发射来自原始Observable的数据,如果原始Observable没有发射数据,就发射一个默认数据SequenceEqual
— 判断两个Observable是否按相同的数据序列SkipUntil
— 丢弃原始Observable发射的数据,直到第二个Observable发射了一个数据,然后发射原始Observable的剩余数据SkipWhile
— 丢弃原始Observable发射的数据,直到一个特定的条件为假,然后发射原始Observable剩余的数据TakeUntil
— 发射来自原始Observable的数据,直到第二个Observable发射了一个数据或一个通知TakeWhile
— 发射原始Observable的数据,直到一个特定的条件为真,然后跳过剩余的数据
算术和聚合操作
这些操作符可用于整个数据序列
Average
— 计算Observable发射的数据序列的平均值,然后发射这个结果Concat
— 不交错的连接多个Observable的数据Count
— 计算Observable发射的数据个数,然后发射这个结果Max
— 计算并发射数据序列的最大值Min
— 计算并发射数据序列的最小值Reduce
— 按顺序对数据序列的每一个应用某个函数,然后返回这个值Sum
— 计算并发射数据序列的和
连接操作
一些有精确可控的订阅行为的特殊Observable
Connect
— 指示一个可连接的Observable开始发射数据给订阅者可连接的Observable (connectable Observable)与普通的Observable差不多,不过它并不会在被订阅时开始发射数据,而是直到使用了Connect操作符时才会开始。用这个方法,你可以等待所有的观察者都订阅了Observable之后再开始发射数据。
RxJava中connect是ConnectableObservable接口的一个方法,使用publish操作符可以将一个普通的Observable转换为一个ConnectableObservable。
举例
ConnectableObservable<String> connectableObservable = Observable.just("a", "c", "d").publish();
connectableObservable.subscribe(new Consumer<String>() {
@Override
public void accept(@NonNull String s) throws Exception {
LogUtil.i(s);
}
}); LogUtil.i("subscribe end....."); Observable.timer(3, TimeUnit.SECONDS).subscribe(new Consumer<Long>() {
@Override
public void accept(@NonNull Long aLong) throws Exception {
LogUtil.i("connect method called after 3 seconds.");
connectableObservable.connect();
}
});复制代码
03-20 15:54:19.328 27493-27493/me.sunbird.react_native_demo I/x_log:RxJavaActivity.testConnectableObservable(L:586): subscribe end.....
03-20 15:54:22.378 27493-27573/me.sunbird.react_native_demo I/x_log:RxJavaActivity$34.accept(L:591): connect method called after 3 seconds.
03-20 15:54:22.419 27493-27573/me.sunbird.react_native_demo I/x_log:RxJavaActivity$33.accept(L:582): a
03-20 15:54:22.419 27493-27573/me.sunbird.react_native_demo I/x_log:RxJavaActivity$33.accept(L:582): c
03-20 15:54:22.420 27493-27573/me.sunbird.react_native_demo I/x_log:RxJavaActivity$33.accept(L:582): d
Publish
— 将一个普通的Observable转换为可连接的RefCount
— 使一个可连接的Observable表现得像一个普通的ObservableReplay
— 确保所有的观察者收到同样的数据序列,即使他们在Observable开始发射数据之后才订阅
转换操作
操作符决策树
几种主要的需求
- 直接创建一个Observable(创建操作)
- 组合多个Observable(组合操作)
- 对Observable发射的数据执行变换操作(变换操作)
- 从Observable发射的数据中取特定的值(过滤操作)
- 转发Observable的部分值(条件/布尔/过滤操作)
- 对Observable发射的数据序列求值(算术/聚合操作)
Scheduler(调度器)中文文档
本质上RxJava就是一个做异步开发的框架,能使我们极其灵活的进行线程切换。
我们可以使用ObserveOn和SubscribeOn操作符,可以让Observable在一个特定的调度器上执行,ObserveOn指示一个Observable在一个特定的调度器上调用观察者的onNext, onError和onCompleted方法,SubscribeOn更进一步,它指示Observable将全部的处理过程(包括发射数据和通知)放在特定的调度器上执行。
subscribeOn 和 observeOn 两个操作符是极其容易混淆的,可以看下这篇博客来彻底分清楚这两个操作符SubscribeOn 和 ObserveOn
调度器类型 | 效果 |
---|---|
Schedulers.computation( ) | 用于计算任务,如事件循环或和回调处理,不要用于IO操作(IO操作请使用Schedulers.io());默认线程数等于处理器的数量 |
Schedulers.from(executor) | 使用指定的Executor作为调度器 |
Schedulers.immediate( ) | 在当前线程立即开始执行任务 |
Schedulers.io( ) | 用于IO密集型任务,如异步阻塞IO操作,这个调度器的线程池会根据需要增长;对于普通的计算任务,请使用Schedulers.computation();Schedulers.io( )默认是一个CachedThreadScheduler,很像一个有线程缓存的新线程调度器 |
Schedulers.newThread( ) | 为每个任务创建一个新线程 |
Schedulers.trampoline( ) | 当其它排队的任务完成后,在当前线程排队开始执行 |
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
LogUtil.w("subscribe method is running in thread:" + Thread.currentThread().getName());
observableEmitter.onNext("a");
observableEmitter.onComplete();
}
}).map(new Function<String, String>() {
@Override
public String apply(@NonNull String s) throws Exception {
LogUtil.w("first map is running in thread:" + Thread.currentThread().getName());
return s;
}
}).subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
LogUtil.w("second map is running in thread:" + Thread.currentThread().getName());
return s;
}
})
.observeOn(Schedulers.io())
.map(new Function<String, String>() {
@Override
public String apply(String s) throws Exception {
LogUtil.w("third map is running in thread:" + Thread.currentThread().getName());
return s;
}
})
.observeOn(Schedulers.computation())
.map(new Function<String, String>() {
@Override
public String apply(@NonNull String s) throws Exception {
LogUtil.w("fourth map is running in thread:" + Thread.currentThread().getName());
return s;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
LogUtil.w("consumer accept method is running in thread:" + Thread.currentThread().getName());
}
});
运行后我们可以得到如下结果:
03-20 11:44:23.716 8687-8723/? W/x_log:RxJavaActivity$27.subscribe(L:528): subscribe method is running in thread:RxCachedThreadScheduler-1
03-20 11:44:23.717 8687-8723/? W/x_log:RxJavaActivity$28.apply(L:535): first map is running in thread:RxCachedThreadScheduler-1
03-20 11:44:23.721 8687-8724/? W/x_log:RxJavaActivity$29.apply(L:543): second map is running in thread:RxNewThreadScheduler-1
03-20 11:44:23.726 8687-8725/? W/x_log:RxJavaActivity$30.apply(L:551): third map is running in thread:RxCachedThreadScheduler-2
03-20 11:44:23.729 8687-8726/? W/x_log:RxJavaActivity$31.apply(L:559): fourth map is running in thread:RxComputationThreadPool-1
03-20 11:44:23.836 8687-8687/? W/x_log:RxJavaActivity$32.accept(L:567): consumer accept method is running in thread:main
RxJava 的使用场景举例
复杂的数据变换
Observable.just("1", "2", "2", "3", "4", "5")//创建Observable
.map(Integer::parseInt)//对每一项执行Integer.parseInt方法
.filter(s -> s > 1)//过滤出所有值 >1 的对象
.distinct() //去重,这里也可以传递一个方法,来定义两个对象是否equals的策略,非常灵活
.take(3)//取到前3个
.reduce((sum, item) -> sum + item) //累加
.subscribe(System.out::println);//9 打印出最终累加的结果。复制代码
Retrofit结合RxJava做网络请求框架
这里不作详解,具体的介绍可以看扔物线的这篇文章,对RxJava的入门者有很大的启发。其中也讲到了RxJava和Retrofit如何结合来实现更简洁的代码
RxJava代替EventBus进行数据传递
RxBus
RxBus并不是一个库,而是一种模式,是使用了RxJava的思想来达到EventBus的数据传递效果。这篇文章把RxBus讲的比较详细。
square/Otto 对 RxBus 的态度
This project is deprecated in favor of RxJava and RxAndroid. These projects permit the same event-driven programming model as Otto, but they’re more capable and offer better control of threading.
为了支持 RxJava 和 RxAndroid,我们已经废弃了这个项目。这两个项目提供了和 Otto 一样的基于事件驱动的编程模型,而且他们更强大,并提供更好的线程控制。
If you’re looking for guidance on migrating from Otto to Rx, this post is a good start.
如果你正在寻找从 Otto 迁移到 Rx 的教程,阅读这篇文章将会是一个很好的开始。
一个网络请求依赖另外一个网络请求返回的结果。例如:登录之后,根据拿到的token去获取消息列表。
@GET("/token")
public Observable<String> getToken();
@GET("/user")
public Observable<User> getUser(@Query("token") String token, @Query("userId") String userId);
...
getToken()
.flatMap(new Func1<String, Observable<User>>() {
@Override
public Observable<User> call(String token) {
return getUser(token, userId);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});
同一个页面同事发起两个以上的请求,此时对页面中的ProgressBar进行管理,即两个请求只能展示一个ProgressBar,并且所有的请求都结束后,ProgressBar 才能消失
@GET("/date1")
public Observable<String> getDate1();
@GET("/data2")
public Observable<String> getData2()
...
progressDialog.show()
Observable.merge(getData1(), getData2())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable disposable) {
}
@Override
public void onNext(String s) {
// 这里进行结果处理
}
@Override
public void onError(Throwable throwable) {
//error handling
progressDialog.dismiss();
}
@Override
public void onComplete() {
progressDialog.dismiss();
}
});
使用throttleFirst(throttle:节流阀)防止按钮重复点击
RxView.clicks(button)
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe(new Observer<Object>() {
@Override
public void onCompleted() {
log.d ("completed");
}
@Override
public void onError(Throwable e) {
log.e("error");
}
@Override
public void onNext(Object o) {
log.d("button clicked");
}
});复制代码
使用debounce(去抖动)做textSearch
用简单的话讲就是当N个结点发生的时间太靠近(即发生的时间差小于设定的值T),debounce就会自动过滤掉前N-1个结点。
比如在做百度地址联想的时候,可以使用debounce减少频繁的网络请求。避免每输入(删除)一个字就做一次联想
RxTextView.textChangeEvents(inputEditText)
.debounce(400, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<TextViewTextChangeEvent>() {
@Override
public void onCompleted() {
log.d("onComplete");
}
@Override
public void onError(Throwable e) {
log.d("Error");
}
@Override
public void onNext(TextViewTextChangeEvent onTextChangeEvent) {
log.d(format("Searching for %s", onTextChangeEvent.text().toString()));
}
});
RxJava 的生态
rx-preferences -使SharedPreferences支持RxJava
RxAndroid -RxJava的Android拓展
RxLifecycle -帮助使用了RxJava的安卓应用控制生命周期
RxBinding -安卓UI控件的RxJava绑定API
Android-ReactiveLocation -Google Play Service API wrapped in RxJava
storio -支持RxJava的数据库
retrofit -支持RxJava的网络请求库
sqlbrite -支持RxJava的sqlite数据库
RxPermissions -RxJava实现的Android运行时权限控制
reark -RxJava architecture library for Android
frodo -Android Library for Logging RxJava Observables and Subscribers.
RxJava 的现况
- RxJava 最新版 2.0.7(注:不兼容1.x 的版本) 大小 2.1M
- RxAndroid 大小 10k
- 支持Java6以上,Android2.3以上
- github star 22511
RxJava 的未来展望
通用的数据流,强大的操作符,灵活的线程调度,简单完善的异常处理机制,函数式编程等等特性,奠定了RxJava的强大地位。
Android 系统中到处都是异步,比如网络请求,文件读写,数据库查询,系统服务或者第三方SDK服务等,这些异步请求非常耗时,需要在非UI线程中执行,而对UI的修改又必须要在主线程中执行。如果再包含多个异步执行嵌套的话,就会让我们的代码显得凌乱。通过RxJava提供的强大而通用的异步处理机制,可以使我们的代码逻辑更清晰,便于后期的维护。并且现在RxJava的生态越来越大,个人认为,以后所有的涉及异步操作的系统服务,第三方库,第三方服务SDK都会以Observable或类Observable的方式提供给我们调用,而不是像现在这样,让我们传递一个又一个的listener。
分享工具
zeplin(软件演示)
- 方便的效果图管理
- 新文件,修改文件提示
- 自动测量
- 标注评论
- 支持pohtoshop和sketch
- 第一个项目免费
charles(软件演示)
- 简单已用,功能强大
- focus 某一个域名下的请求,方便查找
- 对某个请求修改参数,重新请求,方便调试
- 各种格式良好的response解析
- copy出cURL 格式的请求,方便传递个任何人进行请求模拟
最后,我整理了一份RxJava实战教程,含代码和视频,无偿分享给大家,有需要的朋友【点击我】免费获取。
清晰易懂的RxJava入门实践的更多相关文章
- Rxjs入门实践-各种排序算法排序过程的可视化展示
Rxjs入门实践-各种排序算法排序过程的可视化展示 这几天学习下<算法>的排序章节,具体见对排序的总结,想着做点东西,能将各种排序算法的排序过程使用Rxjs通过可视化的方式展示出来,正好练 ...
- webpack的入门实践,看这篇就够了
webpack的入门实践 我会将所有的读者概括为初学者,即使你可能有基础,学习本节之前我希望你具有一定的JavaScript和node基础 文中的 ... ...代表省略掉部分代码,和上面的代码相同 ...
- 分布式学习系列【dubbo入门实践】
分布式学习系列[dubbo入门实践] dubbo架构 组成部分:provider,consumer,registry,monitor: provider,consumer注册,订阅类似于消息队列的注册 ...
- sass、less和stylus的安装使用和入门实践
刚 开始的时候,说实话,我很反感使用css预处理器这种新玩意的,因为其中涉及到了编程的东西,私以为很复杂,而且考虑到项目不是一天能够完成的,也很少是 一个人完成的,对于这种团队的项目开发,前端实践用c ...
- Django入门实践(三)
Django入门实践(三) Django简单应用 前面简单示例说明了views和Template的工作过程,但是Django最核心的是App,涉及到App则会和Model(数据库)打交道.下面举的例子 ...
- Django入门实践(二)
Django入门实践(二) Django模板简单实例 上篇中将html写在了views中,这种混合方式(指Template和views混在一起)不适合大型开发,而且代码不易管理和维护,下面就用Djan ...
- Django入门实践(一)
Django入门实践(一) Django编程思路+入门 认识Django有一个多月了,我觉得学习Django应该先理清它的编程思路.它是典型的MVC框架(在Django里也称MTV),我觉得Djang ...
- 全文搜索引擎Elasticsearch入门实践
全文搜索引擎Elasticsearch入门实践 感谢阮一峰的网络日志全文搜索引擎 Elasticsearch 入门教程 安装 首先需要依赖Java环境.Elasticsearch官网https://w ...
- 【实战】Docker入门实践二:Docker服务基本操作 和 测试Hello World
操作环境 操作系统:CentOS7.2 内存:1GB CPU:2核 Docker服务常用命令 docker服务操作命令如下 service docker start #启动服务 service doc ...
随机推荐
- 45、screen命令
1.screen命令介绍: 当我们在使用linux远程工具进行远程访问服务器时,进行远程访问的界面往往不能关掉,否则程序将不再运行.而且,程序 在运行的过程中,还必须时刻保证网络的通常,这些条件都很难 ...
- 在spring boot使用总结(九) 使用yaml语言来写配置文件
yaml是专门用来写配置文件的语言.使用yaml来写配置文件扩展性比较强而且十分方便.spring boot支持使用yaml语言来写配置文件,使用snakeyaml库来读取配置文件.spring bo ...
- 二进制方式搭建Kubernetes集群
环境准备: 演练暂时用单节点一台master和一台node节点来进行部署搭建(kubernetes 1.19版本) 角色 IP 组件 master 10.129.246.114 kube-apiser ...
- CTF反序列化逃逸
刷了一下CTF反序列化的题,去年没有好好了解.又补了一次PHP,害太菜了.每天看看别人的博客真的可以鼓舞人.简单记录一下两道字符串逃逸问题 推荐一个反序列化总结的很好的笔记https://www.cn ...
- Maven | 把jar包安装到本地仓库
使用的场景 自己写的工具类想安装到本地 从Maven仓库中下载不下来的jar 使用的步骤 首先要保证自己的Maven配置全局环境变量,如果没有配置过maven全局变量,可以按照下面的步骤配置一下: 先 ...
- Selenium启动Chrome浏览器提示“请停用以开发者模式运行的扩展程序”的解决办法
安装了python selenium,运行下面代码: 1 from selenium import webdriver 2 3 browser = webdriver.Chrome() 4 brows ...
- Linux系统引导过程及排除启动故障
一.Linux操作系统引导过程二.系统初始化进程1.init进程2.Systemd3.Systemd单元类型三.排除启动类故障[1].修复MBR扇区故障(含实验过程)[2].修复GRUB引导故障●方法 ...
- .h .cpp区别
首先,我们可以将所有东西都放在一个.cpp文件内. 然后编译器就将这个.cpp编译成.obj,obj是什么东西? 就是编译单元了.一个程序,可以由一个编译单元组成, 也可以有多个编译单元组成. 如果你 ...
- 使用deepin系统自带命令切割视频使用lossless免费软件切割
1,使用ffmpeg命令:ffmpeg -ss 00:00:10 -i gaodengshuxue.mp4 -vcodec copy -acodec copy -t 00:00:20 output.m ...
- Unittest方法 -- 用例执行顺序
import unittestfrom selenium import webdriverclass F4(unittest.TestCase): @classmethod def setUpClas ...