Rxjava2升级注意事项及适配技巧

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/reakingf/article/details/80034892

RxJava2变化

RxJava2(下面统称为rx2)按照Reactive-Streams specification规范进行了重写,因而与RxJava1(下面统称为rx1)相比有了不少的变化。

  1. 引用包名不同
    import时的包名由rx.*变成了io.reactive.*

  2. 禁止接受null
    在rx1中,Observable可以发射任何类型的值,包括null,因此我们不用当心null的问题,即使是像这样使用也不会有任何问题:Observable.just(null), 但在rx2中不再允许发射null,否则会抛出了NullPointerException,因此在使用rx2时就必须先做好null值的判断,避免NPE。

  3. Observable与Flowable
    在rx1中,比较让人诟病的就是背压问题,rx1部分支持背压,而且不能很好的处理背压问题(即被观察者发射速度太快(例如一些UI事件)以至于它的操作符或订阅者不能及时处理相关消息,进而抛出MissingBackpressureException),因而需要开发者自己控制上流的发射速度或者在下游做好限制处理。不过大部分时候,我们都不需要考虑背压问题,因此,rx2便增加了一个新的被观察者Flowable, Flowable的主要特点便是支持背压,而原先的Observable便更改为不再支持背压了,因此我们便可以根据自己的需求来选择合适的被观察者。

  4. Observer与Suscriber
    与Observable与Flowable对应,它两的订阅者分别是Observer与Suscriber, 即 Observable.subscribe(Observer)Flowable.subscribe(Subscriber) 。因而,Observable在create时不再Subscriber参数,而是一个ObservableEmitter, 即

    Observable.create(new ObservableOnSubscribe<Integer>() {
     @Override
     public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
    
    }
    }) 

    遵循reactive-streams,取消了原先的Suscriber,改用reactive-streams的Suscriber,同时Observer与之保持一致,因此rx2的Observer和Suscriber与rx1的有点不同,即 onNext, onError, onComplete 依然存在着,但 onStart 便不存在了,不过新的 onSubscribe 方法其实也可以替代原先的 onStart ,若依然想用 onStart ,rx2也有提供几个实现Suscriber接口又包含该方法的抽象类:DefaultSubscriberResourceSubscriberDisposableSubscriber

  5. Disposable
    在rx2中,由于引入了reactive-streams,而reactive-streams中已经存在了Subscription, 因而将rx1的Subscription改名为Disposable,依然用来判断和取消订阅关系。
    不过,为了和reactive-streams保持一致,Observable.subscribe(Observer)Flowable.subscribe(Subscriber) 这两种方式不再返回一个Disposable(rx1会返回一个Subscription)对象,取而代之的是提供了subscribe的几个新重载函数,它们便会返回对应的Disposable,分别是

    public final Disposable subscribe()
    
    public final Disposable subscribe(Consumer<? super T> onNext)
    
    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError)
    
    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete)
    
    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe)
    //直接订阅Observer或Subscriber返回void
    public final void subscribe(Observer<? super T> observer)
    public final void subscribe(Subscriber<? super T> s)
  6. Action和Function变更
    rx2根据Java8的命名规范,更改了Action和Function的命名,具体如下:

(Action)rx1 rx2
Action0 Action
Action1 Consumer
Action2 BiConsumer
Action3-Action9 取消
ActionN Consumer<Object[]>
(Function)rx1 rx2
Func0 取消
Func1 Function
Func2 BiFunction
Func3 - Func9 Function3 - Function9
FuncN Function<Object[], R>
Func1<T, Boolean> Predicate<T>

同时,在rx2中,所有的Action和Function都抛出了异常,因此我们并不得不去捕获这些异常了。
7. 一些常用方法的变更
from
rx1中,对于数组,列表和Future类型的数据源都采用from进行获取,而rx2对其进行了细 分,分别拆分为fromArray , fromIterablefromFuture , 并添加 了一个fromPublisher , 至于 fromCallable 则保持不变。
其他
带有2-9个参数的startWith统一改为startWithArray
toList和toMap等的返回值变成Single类型,并添加toObservable方便转化
toBlocking().y变成blockingY(),例如toBlocking().firstOrDefault(defaultValue) 变成 blockingFirst(defaultValue)

以上便是rx2中比较常见的一些变化,其他变化具体请查看RxJava2的变化

NPE适配技巧

由于rx2不再允许接受null值,因此在创建流之前必须先判断该源头数据是否为null,但若是每次创建新的rx流都需要判断一次,特别是对于那些从rx1升到rx2上的项目,由于之前不需考虑null值问题,此时来添加判断,必定是一件十分繁琐又无趣的事,因而我们可以通过创建一个NullCheckUtil工具类在处理这些源头数据,进而大大简化每次创建rx流时的工作。主要分为以下两种情况。

  1. 源头为单一实体
    例如从缓存中获取某个对象,在未保存下来之前肯定是null,也有可能保存了null或保存不成功,因此必须先判断好再创建rx流。对于这种我们认为已经存在的对象,我们通常会采用Observable.just()来开始一个rx流,因此我们可以为该对象添加个外壳,即采用Java8中的Optional来做封装(因为Java8以下的Optional要求Android的API必须在24及以上,因此不要导入Java8以下的,当然若是不想用Java8,也可以直接写个简单的外壳用来封装),然后通过optional.get()来获取我们传入的值,例如:

    Observable.just(Optional.ofNullable(cache))
            .subscribe(new Consumer<Optional<Object>>() {
                    @Override
                    public void accept(Optional<Object> optional) throws Exception {
                        // 通过optional.isPresent()判断是否为null,再通过optional.get() 获取cache
                    }
                });
    
  2. 源头为列表型实体
    例如从API请求一个数据列表,首先获取到的response可能为null,其次response里的数据可能为null,当然如果是有多层嵌套的数据的话,里面的列表也有可能为null,因此可以提供几个统一的处理方法来规避这些问题。先贴上NullCheckUtil的部分代码:

public interface Function<T, R> {
    R apply(T var);
}

// ①
public static <T> List<T> elvisList(List<T> t) {
    if (t == null) return Collections.emptyList();
    return t;
}

// ②
public static <T, R> List<R> elvisList(T t, Function<T, List<R>> ifNotNull) {
    List<R> list = t == null ? new ArrayList<R>() : ifNotNull.apply(t);
    if (list == null) list = Collections.emptyList();
    return list;
}

// ③
public static <T, R> Observable<R> observableElvisList(T t, Function<T, List<R>> ifNotNull) {
    List<R> list = t == null ? new ArrayList<R>() : ifNotNull.apply(t);
    if (list == null) list = Collections.emptyList();
    return Observable.fromIterable(list);
}

整个思路其实很简单,就是判断是否为null,若是null则创建一个新的空列表。方法①最为简单就不说了,方法②适用于数据源为嵌套于某个对象里面的列表,至于如何获取该列表,便通过Function接口由外部提供,方法③则是在②的基础上直接返回一个Observable,其实对于我们这里说的rx2来说,可以直接使用方法③作为列表型数据源的处理,此时我们便可以这样使用:

NullCheckUtil.observableElvisList(apiResponseData, data -> data.list).subscribe();

当然这里采用的是lambda的写法,所以看起来非常整洁,这里也强力建议使用lambda。这样我们便不需要再去考虑数据源是否为null的问题了,可以无忧无虑的直接创建rx流,是不是极其方便简单呢,当然,如果有其他通用的操作,例如map/filter/toList/compose之类的,也可以直接抽取到公共的工具类里,这样便可以进一步简化代码了。

猜你喜欢

转载自blog.csdn.net/reakingf/article/details/80034892