清晰易懂的RxJava入门实践(1)

@Override

public void subscribe(ObservableEmitter e) throws Exception {

for (File file : files) {

e.onNext(file);

}

e.onComplete();

}

});

//创建Observer

Observer observer = new Observer() {

@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>() {//分别获取每个文件夹下面的文件,组合成一个Observable

@Override

public Observable 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 consumer=new Consumer() {

@Override

public void accept(@NonNull Bitmap bitmap) throws Exception {

imageCollectorView.addImage(bitmap);

}

};

//对事件集进行处理并连接消费者

observable.flatMap(new Func1<File, Observable>() {//分别获取每个文件夹下面的文件,组合成一个Observable

@Override

public Observable 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>() {

@Override

public Observable 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() {

@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(轻量级加密解密工具包) 这个包报出了内存溢出的异常,所以我感觉现在还不太稳定。
  • 当然我们也可以不用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 — 通过调用观察者的方法从头创建一个Observable

create操作符是所有创建型操作符的“根”,也就是说其他创建型操作符最后都是通过create操作符来创建Observable的

  • From — 将其它的对象或数据结构转换为Observable

  • Just — 将对象或者对象集合转换为一个会发射这些对象的Observable

  • Defer — 在观察者订阅之前不创建这个Observable,为每一个观察者创建一个新的Observable

  • Empty/Never/Throw — 创建行为受限的特殊Observable

一般用于测试

  • Interval — 创建一个定时发射整数序列的Observable

  • Range — 创建发射指定范围的整数序列的Observable

  • Repeat — 创建重复发射特定的数据或数据序列的Observable

  • Start — 创建发射一个函数的返回值的Observable

  • Timer — 创建在一个指定的延迟之后发射单个数据的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发射的数据组合在一起,然后将这个函数的结果作为单项数据发射

错误处理

这些操作符用于从错误通知中恢复

  • Catch — 捕获,继续序列操作,将错误替换为正常的数据,从onError通知中恢复

  • Retry — 重试,如果Observable发射了一个错误通知,重新订阅它,期待它正常终止

辅助操作

一组用于处理Observable的操作符

  • Delay — 延迟一段时间发射结果数据

  • Do — 注册一个动作占用一些Observable的生命周期事件,相当于Mock某个操作

  • Materialize/Dematerialize — 将发射的数据和通知都当做数据发射,或者反过来

  • ObserveOn — 指定观察者观察Observable的调度程序(工作线程)

  • SubscribeOn — 指定Observable应该在哪个调度程序上执行

  • Serialize — 强制Observable按次序发射数据并且功能是有效的

  • Subscribe — 收到Observable发射的数据和通知后执行的操作

  • TimeInterval — 将一个Observable转换为发射两个数据之间所耗费时间的Observable

  • Timeout — 添加超时机制,如果过了指定的一段时间没有发射数据,就发射一个错误通知

  • 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 connectableObservable = Observable.just(“a”, “c”, “d”).publish();

connectableObservable.subscribe(new Consumer() {

@Override

public void accept(@NonNull String s) throws Exception {

LogUtil.i(s);

}

});

LogUtil.i(“subscribe end…”);

Observable.timer(3, TimeUnit.SECONDS).subscribe(new Consumer() {

@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表现得像一个普通的Observable

  • Replay — 确保所有的观察者收到同样的数据序列,即使他们在Observable开始发射数据之后才订阅

转换操作
  • To — 将Observable转换为其它的对象或数据结构

  • Blocking 阻塞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() {

@Override

public void subscribe(ObservableEmitter 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() {

@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.

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

image

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

image

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

,并提供更好的线程控制。

If you’re looking for guidance on migrating from Otto to Rx, this post is a good start.

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

[外链图片转存中…(img-z9D3Aafh-1714664556207)]

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

[外链图片转存中…(img-9TBxJQqI-1714664556208)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 22
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值