5.1 RxJava1.x自定义操作符与原理分析

欢迎大家加入QQ群一起讨论: 489873144(android格调小窝)
我的github地址:https://github.com/jeasonlzy

概念

Rxjava的操作符分两种
- 一种是对一条数据链中的数据流进行转换
- 另一种是对整条链本身进行转换

一、第一种

对一条数据链中的数据流进行转换

1. 操作符分析

在讲自定义操作符之前,我们不直接来结论如何实现,我们先分析源码,为什么可以这么做,看懂了原因,当然自己写起来就得心应手,而且不容易出错。

我们先看看rxjava已经提供的操作符是如何实现的,先看最常用的map操作符,接受一个转换函数,重新生成一个OnSubscribe对象,OnSubscribe对象是我们发送事件的逻辑,相当于把我们的事件发送包装了一层

这里面有个OnSubscribeMap对象,看名字就知道是Map操作符的实现,我们看看代码,为了更方便的看清逻辑,我将源码稍微简化,并整理,核心逻辑是一样,如下:

public final class OnSubscribeMap<T, R> implements OnSubscribe<R> {

  final Observable<T> source;
  final Func1<? super T, ? extends R> transformer;

  public OnSubscribeMap(Observable<T> source, Func1<? super T, ? extends R> transformer) {
      this.source = source;
      this.transformer = transformer;
  }

  @Override
  public void call(final Subscriber<? super R> o) {
      Subscriber<T> parent = new Subscriber<T>() {
          @Override
          public void onNext(T t) {
              // 注意看这里,调用了外部的转换函数后得到一个新值,回调给原始的观察者
              R mapper = transformer.call(t);
              o.onNext(mapper);
          }

          @Override
          public void onCompleted() {
              o.onCompleted();
          }

          @Override
          public void onError(Throwable e) {
              o.onError(e);
          }
      };
      //最后,用原始的Observable对象订阅新的观察者
      source.unsafeSubscribe(parent);
  }
}

我们发现原理很简单,用外部传进来的转换函数进行变化,得到一个新的值,继续回调给原始的观察者,最后,用原始的Observable对象订阅新的观察者,这样,一旦这个Observable被订阅,那么一定会先执行到这个生成的parent观察者,然后执行转换函数,将转换后的结果回调给目标观察者。一个map操作符就这么简单的完成了。

2. lift变换

我们看到前面分析的内容,知道这个是rxjava默认提供的操作符,而这个方法是Observable对象的方法,我们要是自定义操作符的话总不能改源码,直接在Observable里面添加方法把。所以这个时候就需要一层抽取,把和自定义相关的内容通过接口的形式让外界传递进来。那么这个接口如何定义呢?我们来观察上面map操作符的源码。

其实我们需要做两件事就可以完成自定义的操作符
1. 根据外界传递的方法或者参数,用原始Subscriber生成一个新的Subscriber对象
2. 使用原始的Observable订阅新的Subscriber

这里面第二步永远是不变的,那么我们需要外界传递的就是第一部分。
所以我们定义一个接口如下:

public interface Func1<T, R> extends Function {
    R call(T t);
}

public interface Operator<R, T> extends Func1<Subscriber<? super R>, Subscriber<? super T>> {

}

上面是源码中的定义方法,接受一个Subscriber<T>类型的观察者,返回一个Subscriber<R>类型的观察者,简化后的代码如下,是不是完美的表达第一步想要干的事情,这就是抽象思维,真是伟大!

public interface Operator<R, T> extends Func1<Subscriber<R>, Subscriber<T>> {
    Subscriber<R> call(Subscriber<T> subscriber);
}

好,抽象结束,我们总该需要一个地方来完成所有的逻辑吧,这个时候就需要在Observable中定义一个方法了,我们叫做lift,他需要接受一个Operator接口,然后返回我们需要的Observable对象,源码如下:

看着是不是和前面的map逻辑一样,没错,既然转换接口有了,我们就要实现第二步的逻辑了,我们发现定义了一个类,叫做OnSubscribeLift,跟代码看看

发现了什么,是不是很明显的两步,第一步调用接口的转换函数,将老的Subscriber也就是o,转成新的Subscriber对象,也就是st,然后使用原始的Observable对象,也就是parent,订阅了新的Subscriber对象st,其余的都是异常处理,不是核心逻辑。

值得注意的是,在这两步中间多了一个方法,叫onStart,这个也是很多博客中会讲到的,他是Subscriber相比Observer新增加的一个方法,用于在发送事件之前回调做点事情,但是无法切换线程,他永远和事件的订阅保持在同一个线程,这个源码就是最好的答案。

3. 自定义操作符

现在终于有了lift方法,自定义的操作符说到底就是lift方法中的Operator接口的实现。

我们撸起袖子,搓搓手掌,开始实战,就使用lift来实现一个map的全部功能,废话不多说,直接上代码,创建一个类OperatorMapTest,实现Operator接口:

构造函数传递一个转换函数,在call方法中,创建一个新的Subscriber并返回,在每个方法的内部实现自己的转换逻辑。

再看如何使用它呢,如下:

是不是特别简单,突然感觉操作符也就这么回事,要是不知道原理还以为有多么神奇呢,是吧,在想想filter操作符呢,做过滤用的,是不是也可以很简单的实现,我们再来一次,创建一个类OperatorFilterTest,实现Operator接口:

使用如下:

二、第二种

整条链本身进行转换

我们写代码经常有这种需求,需要让网络请求在io线程执行,回调在主线程执行,我们把这个场景叫做一组变换,在很多场景下需要的都是同一组变换,如果我们每个地方都这么写一遍就显得太low了,但是如果定义一个方法,在这个方法中写一份逻辑,这样的话又会让链试调用断开,不够优雅,所以对于这种需求我们可以继续抽象。

我们需要传递一个Observable,然后把这个Observable进行我们需要的变换后,再返回这个变换后的Observable。

这个就是总结出来的需求,怎么做呢?同样,抽象出一个接口,万能的抽象,再次膜拜

public interface Func1<T, R> extends Function {
    R call(T t);
}

public interface Transformer<T, R> extends Func1<Observable<T>, Observable<R>> {

}

上面是源码中的定义方法,接受一个Observable<T>类型的观察序列,返回一个Observable<R>类型的观察序列,简化后的代码如下,是不是再一次完美的表达了需求。

public interface Transformer<T, R> extends Func1<Observable<T>, Observable<R>> {
    Observable<R> call(Observable<T> observable);
}

接口抽象后,下一步就是实现逻辑,源码定义了一个compose方法,看源码如下:

简单的就一行代码,直接调用接口的call方法,并返回,没了!!!

这么简单的话我们快实战试试,定义一个类叫TransformerSchedule,如下

使用如下:

看完这个感觉更简单了,当然我只是举个例子可以做线程切换,其实你完全可以写很复杂的业务逻辑,这里面逻辑有共性,就用一个Transformer去实现,理解了原理,怎么高兴怎么来。

三、总结

rxjava仅仅核心库的操作符目前就多达200多个,这么多的操作符基本满足了绝大部分的需求,详细每个操作符具体的应用场景可以参考本系列第二篇文章,当然你有特殊的需求,现有的无法满足你,那就需要自定义了。还是那句话,理解原理,理解思想,再复杂再神奇的功能也能用很简单的代码实现。

思考题?

我们在创建一个Observable的时候,会创建一个匿名内部类,并实现call方法,这个方法里面传递了一个参数Subscriber,那么这个Subscriber是哪里来的,他是如何创建的?

以上问题就是在使用操作符的过程中,关于事件的通知顺序与回调顺序的一个难点,搞清楚这个问题后,对后续分析RxJava的线程调度原理有很大帮助,可以自己思考下,如果有难度,可以结合下面的图片看源码,也能找到答案。

以下图片是博客何时夕RxJava源代码剖析中,关于事件流程的一个分析,我这里借用一下,感谢作者。

如果你觉得好,对你有过帮助,请给我一点打赏鼓励吧,一分也是爱呀!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值