RxJava学习详解(二)

来源:互联网 发布:莫文蔚周星驰知乎 编辑:程序博客网 时间:2024/05/18 11:13

RxJava学习详解(二)

在 RxJava 的默认规则中,事件的发出和消费都是在同一个线程的。也就是说,如果只用上面的方法,实现出来的只是一个同步的观察者模式。观察者模式本身的目的就是『后台处理,前台回调』的异步机制,因此异步对于 RxJava 是至关重要的。而要实现异步,则需要用到 RxJava 的另一个概念: Scheduler 。

1、线程控制 —— Scheduler (一)

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

(1) Scheduler 的 API (一)

在RxJava 中,Scheduler ——调度器,相当于线程控制器,RxJava通过它来指 定每一段代码应该运行在什么样的线程。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。
  • 另外, Android 还有一个专用的AndroidSchedulers.mainThread(),他指 定的操作将在 Android 主线程运行。

    有了这几个 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。 subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。 observeOn(): 指定 Subscriber所运行在的线程。或者叫做事件的消 费的线程。
  1. Observable.just(1,2,3,4)
  2. .subscribeOn(Schedulers.io())//指定subscribe()发生在IO线程
  3. .observeOn(AndroidSchedulers.mainThread())//指定Subscriber的回调发生在主线程
  4. .subscribe(new Action1<Integer>() {
  5. @Override
  6. public void call(Integer integer) {
  7. Log.d("Schedulers使用","integer"+integer);
  8. }
  9. });

打印结果如下:

D/Schedulers使用: integer1D/Schedulers使用: integer2D/Schedulers使用: integer3D/Schedulers使用: integer4 上面这段代码中,由于 subscribeOn(Schedulers.io())的指定,被创建 的事件的内容 1、2、3、4 将会在 IO 线程发出;而由于 observeOn(AndroidScheculers.mainThread()) 的指定,因此 subscriber 数字的打印将发生在主线程 。事实上,这种在 subscribe() 之前写上两句 subscribeOn(Scheduler.io()) 和 observeOn(AndroidSchedulers.mainThread())的使用方式非常常见,它适用于多数的『后台线程取数据,主线程显示』的程序策略。

2、变换

RxJava 提供了对事件序列进行变换的支持,这是它的核心功能之一,也是大多数人说『RxJava 真是太好用了』的最大原因。所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。概念说着总是模糊难懂的,来看 API。

(1)变换操作符

1、Buffer操作符

buffer操作符周期性的收集源Observable产生的结果到列表中,并把这个列表提交给订阅者,订阅者处理后,清空buffer列表,同时接收下一次收集的结果提交给订阅者,周而复始。注意:一旦源Observable在产生结果中出现异常,即使buffer已经存在收集到的结果,订阅者也会马上收到这个异常,并结束整个过程。buffer操作符有两个参数,分别为count和skip,count参数指定buffer操作符的大小,skip参数用来指定每次发射一个集合需要跳过几个数

eg:

  1. Observable.just(1,2,3,4,5,6,7,8)
  2. .buffer(3,2)
  3. .subscribe(new Action1<List<Integer>>() {
  4. @Override
  5. public void call(List<Integer> integers) {
  6. Log.d("buffer","buffer"+integers);
  7. }
  8. });

打印结果:

D/buffer: buffer[1, 2, 3]D/buffer: buffer[3, 4, 5]D/buffer: buffer[5, 6, 7]D/buffer: buffer[7, 8]

2、flatMap操作符

flatMap操作符是把Observable产生的结果转换成多个Observable,然后把这多个Observable"扁平化"成一个Observable,并依次提交产生的结果给订阅者

eg:从网络获取一组用户信息,然后在分别遍历每个用户的名称

  1. //flatMap()
  2. getNetData().flatMap(new Func1<List<String>, Observable<String>>() {
  3. @Override
  4. public Observable<String> call(List<String> strings) {
  5. return Observable.from(strings);
  6. }
  7. }).subscribe(new Action1<String>() {
  8. @Override
  9. public void call(String s) {
  10. Log.d("flatMap:","flatMap:" + s);
  11. }
  12. });
  13. public Observable<List<String>> getNetData(){
  14. return Observable.create(new Observable.OnSubscribe<List<String>>() {
  15. @Override
  16. public void call(Subscriber<? super List<String>> subscriber) {
  17. List<String> list = new ArrayList<String>();
  18. list.add("java");
  19. list.add("Android");
  20. list.add("RxJava");
  21. subscriber.onNext(list);
  22. }
  23. });
  24. }

打印结果如下:

D/flatMap:: flatMap:javaD/flatMap:: flatMap:AndroidD/flatMap:: flatMap:RxJava

flagMap参数分析:

  1. flatMap(new Func1<List<String>, Observable<String>>() {
  2. @Override
  3. public Observable<String> call(List<String> strings) {
  4. return Observable.from(strings);
  5. }
  6. }
可以看到flatmap的参数 Func1的泛型约束有两个 分别是 List<String> 和 Observable<String> 这两个泛型分别是约束的call方法的参数和返回值类型,而List<String> 是上面传来的数据,Observable<String>转换后返 回的数据这里就是将List<String> 转为Observable<String>.

eg:我需要在接口A中去获取我的id,然后在根据我的id去接口B中获取我的信息

  1. getNetUserId().flatMap(new Func1<String, Observable<String>>() {
  2. @Override
  3. public Observable<String> call(String s) {
  4. return getNetUserName(s);
  5. }
  6. }).subscribe(new Action1<String>() {
  7. @Override
  8. public void call(String s) {
  9. Log.d("flatMap2:", "flatMap2:" + s);
  10. }
  11. });
  12. public Observable<String> getNetUserName(final String id) {
  13. return Observable.create(new Observable.OnSubscribe<String>() {
  14. @Override
  15. public void call(Subscriber<? super String> subscriber) {
  16. if (id.equals("1001")) {
  17. subscriber.onNext("java来了");
  18. } else {
  19. subscriber.onNext("无内容");
  20. }
  21. subscriber.onCompleted();
  22. }
  23. });
  24. }
  25. public Observable<String> getNetUserId() {
  26. return Observable.create(new Observable.OnSubscribe<String>() {
  27. @Override
  28. public void call(Subscriber<? super String> subscriber) {
  29. subscriber.onNext("1001");
  30. }
  31. });
  32. }

打印结果如下:

D/flatMap2:: flatMap2:java来了

3、map操作符

Map操作符的功能类似于FlatMap,不同之处在于它对数据的转化是直接进行的,而FlatMap需要通过一些中间的Observables来进行。

eg:

  1. //map()
  2. Observable.just(new StringBuffer("java"), new StringBuffer("Android"), new StringBuffer("RxJava"))
  3. .map(new Func1<StringBuffer, String>() {
  4. @Override
  5. public String call(StringBuffer stringBuffer) {
  6. return replaceName(stringBuffer);
  7. }
  8. }).subscribe(new Action1<Object>() {
  9. @Override
  10. public void call(Object o) {
  11. Log.d("map:","map:"+o);
  12. }
  13. });
  14. private String replaceName(StringBuffer name) {
  15. int index = name.indexOf("Android");
  16. if(index != -1){
  17. name.replace(index,7,"**");
  18. }
  19. return name.toString();
  20. }

打印结果如下:

D/map:: map:javaD/map:: map:**D/map:: map:RxJava这里出现了一个叫做Func1的类。它和Action1非常相似,也是RxJava的一 个接口,用于包装含有一个参数的方法。Func1和Action的区别在于,Func1 包装的是有返回值的方法。另外,和ActionX一样,FuncX也有多个,用于不同 参数个数的方法。FuncX和ActionX的区别在FuncX包装的是有返回值的方法。可以看到,map()方法将参数中的StringBuffer对象转换成一个String对 象后返回,而在经过map()方法后,事件的参数类型也由String转为了 StringBuffer。这种直接变换对象并返回的,是最常见的也最容易理解的变换。不过 RxJava 的变换远不止这样,它不仅可以针对事件对象,还可以针对整个事件队列,这使得 RxJava 变得非常灵活。map(): 事件对象的直接变换,具体功能上面已经介绍过。它是 RxJava 最常用的变换。

4、scan操作符

Scan操作符对原始Observable发射的第一项数据应用一个函数,然后将那个函数的结果作为自己的第一项数据发射。它将函数的结果同第二项数据一起填充给这个函数来产生它自己的第二项数据。它持续进行这个过程来产生剩余的数据序列。这个操作符在某些情况下被叫做accumulator。类似于递归。

eg:

  1. Observable.just(1,2,3,4,5,6).scan(new Func2<Integer, Integer, Integer>() {
  2. @Override
  3. public Integer call(Integer integer, Integer integer2) {
  4. return integer + integer2;
  5. }
  6. }).subscribe(new Action1<Integer>() {
  7. @Override
  8. public void call(Integer integer) {
  9. Log.d("scan","scan"+integer);
  10. }
  11. });

打印结果如下:

D/scan: scan1 D/scan: scan3 D/scan: scan6 D/scan: scan10 D/scan: scan15 D/scan: scan21

5、window操作符

window操作符与buffer操作符类似,区别在于buffer操作符产生的结果是一个List缓存,而window操作符产生的结果是一个Observable,订阅者可以对这个结果Observable重新进行订阅处理。
  1. Observable.interval(1, TimeUnit.SECONDS)
  2. .take(12)
  3. .window(3,TimeUnit.SECONDS)
  4. .subscribe(new Action1<Observable<Long>>() {
  5. @Override
  6. public void call(Observable<Long> longObservable) {
  7. Log.d("window","window===start");
  8. longObservable.subscribe(new Action1<Long>() {
  9. @Override
  10. public void call(Long aLong) {
  11. Log.d("window","window"+aLong);
  12. }
  13. });
  14. }
  15. });

运行结果如下:

D/window: window0 D/window: window1 D/window: window===start D/window: window2 D/window: window3 D/window: window4 D/window: window===start D/window: window5 D/window: window6 D/window: window7D/window: window===start D/window: window8 D/window: window9 D/window: window10 D/window: window===startD/window: window11

(2)变换的原理:

以上各种变换符虽然功能各有不同,但实质上都是针对事件序列的处理和再发送。而在 RxJava 的内部,它们是基于同一个基础的变换方法: lift(Operator)。首先看一下 lift() 的内部实现(仅核心代码):
  1. // 注意:这不是 lift() 的源码,而是将源码中与性能、兼容性、扩展性有关的代码剔除后的核心代码。
  2. // 如果需要看源码,可以去 RxJava GitHub 仓库下载。
  3. public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
  4. return Observable.create(new OnSubscribe<R>() {
  5. @Override
  6. public void call(Subscriber subscriber) {
  7. Subscriber newSubscriber = operator.call(subscriber);
  8. newSubscriber.onStart();
  9. onSubscribe.call(newSubscriber);
  10. }
  11. });
  12. }
这段代码很有意思:它生成了一个新的 Observable 并返回,而且创建新 Observable 所用的参数 OnSubscribe 的回调方法 call() 中的实现竟然看起来和前面讲过的 Observable.subscribe() 一样!然而它们并不一样哟~不一样的地方关键就在于第二行 onSubscribe.call(subscriber) 中的 onSubscribe 所指代的对象不同(高能预警:接下来的几句话可能会导致身体的严重不适)
  • subscribe() 中这句话的 onSubscribe 指的是 Observable 中的 onSubscribe 对象,这个没有问题,但是 lift() 之后的情况就复杂了点。
  • 当含有 lift() 时: 
    1.lift() 创建了一个 Observable 后,加上之前的原始Observable,已经有 两个 Observable 了; 
    2.而同样地,新 Observable 里的新 OnSubscribe 加上之前的原始 Observable 中的原始 OnSubscribe,也就有了两个 OnSubscribe; 
    3.当用户调用经过 lift() 后的 Observable 的 subscribe() 的时候,使用的是 lift() 所返回的新的 Observable ,于是它所触发的 onSubscribe.call(subscriber),也是用的新 Observable 中的新 OnSubscribe,即在 lift() 中生成的那个 OnSubscribe; 
    4.而这个新 OnSubscribe 的 call() 方法中的 onSubscribe ,就是指的原始 Observable 中的原始 OnSubscribe ,在这个 call() 方法里,新 OnSubscribe 利用 operator.call(subscriber) 生成了一个新的 Subscriber(Operator 就是在这里,通过自己的 call() 方法将新 Subscriber 和原始 Subscriber 进行关联,并插入自己的『变换』代码以实现变换),然后利用这个新 Subscriber 向原始 Observable 进行订阅。 
    这样就实现了 lift() 过程,有点像一种代理机制,通过事件拦截和处理实现事件序列的变换。
1 0