RxJava 转换操作符详解及lift 源码解析

RxJava 提供了对事件序列进行变换的支持,这是它的核心功能之一,也是大多数人说『RxJava 真是太好用了』的最大原因。所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。特别是map、flatMap、concatMap都是接受一个函数作为参数(Func1),非常常用,但很多人不知道它们通过 lift() 实现。

目录

1. map

2. flatMap

3. concatMap

4. flatMapIterable

5. SwitchMap

6. scan

7. GroupBy

8. compose

9. lift()源码


1. map

 

官方的图(正确理解:一种颜色代表一条数据,一个圈转变为一个正方形),要点:

  • map 转换是一对一的,原来发射了几个数据,转换之后还是几个
  • map 转换可以改变发射的数据类型

 map应用场景非常广泛,这里不再列举,先看个例子吧

//String转Integer
Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                subscriber.onNext("123456");
                subscriber.onComplete();
            }
        }).map(new Func1<String, Integer>() {
            @Override
            public Integer call(String s) {
                return Integer.parseInt(s);
            }
        }).subscribe(new Consumer<String>() {
            @Override
            public void accept(@NonNull Integer integer) {
                
            }
        });

2. flatMap

 flatMap() 中的 flat 就是所谓『铺平』。将一个发射数据的Observable变换为多个Observables,然后将它们发射的数据合并后放进一个单独的Observable(统一路径)。

原理

  1. 传入的事件对象装换成一个Observable对象
  2. 这是不会直接发送这个Observable, 而是将这个Observable激活让它自己开始发送事件
  3. 每一个创建出来的Observable发送的事件,都被汇入同一个Observable,这个Observable负责将这些事件统一交给Subscriber的回调方法。

理解下图精髓:一种颜色的圆代表源数据(Data<List<Item>>),flat map后,每种颜色分裂为多方块(方块代表Item),分裂后的数据是乱序的


应用场景

  • 循环嵌套(循环套循环)
  • 连续请求两个接口(第一个接口的返回值是第二个接口的请求参数),以前我们会在一个请求完成后,在onResponse中获取结果再请求另一个接口。这种接口嵌套,代码看起来是非常丑陋的
public class Student {
    public String name;
    public int id;
    public List<Source> mSources;
}
public class Source {
    public int sourceId;//id
    public String name;//课程名
    public int score;//成绩
}
//示例
Flowable.fromIterable(MockData.getAllStudentInfoById(0))
            .flatMap(new Function<Student, Publisher<Source>>() {
                @Override
                public Publisher<Source> apply(@NonNull Student student) throws Exception {
                    return Flowable.fromIterable(student.mSources);
                }
            })   
            .subscribe(new Consumer<Source>() {
                @Override
                public void accept(@NonNull Source source) throws Exception {
                    String content = "sourceName:"+source.name +" source score:"+source.score;
                    Log.i(TAG,content);

                }
            });

3. concatMap

concatMap()解决了flatMap()的交叉(无序)问题,它能够把发射的值连续在一起,用法及应用场景可以参考flatMap。

图解不再赘述了!!

 

4. flatMapIterable

flatMapIterable()flatMap()几乎是一样的,不同的是flatMapIterable()它转化的多个Observable是使用Iterable作为源数据的。

Observable.from(communities)
        .flatMapIterable(new Func1<Community, Iterable<House>>() {
            @Override
            public Iterable<House> call(Community community) {
                return community.houses;
            }
        })
        .subscribe(new Action1<House>() {

            @Override
            public void call(House house) {

            }
        });

5. SwitchMap

switchMap()flatMap()很像,除了一点:每当源Observable发射一个新的数据项(Observable)时,它将取消订阅并停止监视之前那个数据项产生的Observable,并开始监视当前发射的这一个。

 

6. scan

scan()对一个序列的数据(list/array)应用一个函数(fn1),并将这个函数的结果发射出去作为下次运用 fn1 的第一个参数使用。

Observable.just(1, 2, 3, 4, 5)
        .scan(new Func2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer integer, Integer integer2) {
                return integer + integer2;
            }
        }).subscribe(new Action1<Integer>() {
    @Override
    public void call(Integer integer) {
        System.out.print(integer+“ ”);
    }
});
/** 输出结果:
1 3 6 10 15
**/

7. GroupBy

groupBy()将原始Observable发射的数据按照key来拆分成一些小的Observable,然后这些小Observable分别发射其所包含的的数据,和SQL中的groupBy类似。实际使用中,我们需要提供一个生成key的规则(也就是Func1中的call方法),所有key相同的数据会包含在同一个小的Observable中。另外我们还可以提供一个函数来对这些数据进行转化,有点类似于集成了flatMap。

/**
假设我现在有一组房源List<House> houses,每套房子都属于某一个小区,现在我们需要根据小区名来对房源进行分类,然后依次将房源信息输出。
**/
List<House> houses = new ArrayList<>();
houses.add(new House("中粮·海景壹号", "中粮海景壹号新出大平层!总价4500W起"));
houses.add(new House("竹园新村", "满五唯一,黄金地段"));
houses.add(new House("中粮·海景壹号", "毗邻汤臣一品"));
houses.add(new House("竹园新村", "顶层户型,两室一厅"));
houses.add(new House("中粮·海景壹号", "南北通透,豪华五房"));
Observable<GroupedObservable<String, House>> groupByCommunityNameObservable = Observable.from(houses)
        .groupBy(new Func1<House, String>() {

            @Override
            public String call(House house) {
                return house.communityName;
            }
        });
//GroupedObservable是一个特殊的Observable,它基于一个分组的key,在这个例子中的key就是小区名
Observable.concat(groupByCommunityNameObservable)
        .subscribe(new Action1<House>() {
            @Override
            public void call(House house) {
                System.out.println("小区:"+house.communityName+"; 房源描述:"+house.desc);
            }
        });
/**
小区:中粮·海景壹号; 房源描述:中粮海景壹号新出大平层!总价4500W起
小区:中粮·海景壹号; 房源描述:毗邻汤臣一品
小区:中粮·海景壹号; 房源描述:南北通透,豪华五房
小区:竹园新村; 房源描述:满五唯一,黄金地段
小区:竹园新村; 房源描述:顶层户型,两室一厅
**/

8. compose

Observable 还有一个变换方法叫做 compose(Transformer实际上就是一个Func1<Observable<T>, Observable<R>>,可以通过它将一种类型的Observable 转换成另一种类型的Observable )。它和 lift() 的区别在于: lift() 是针对事件项和事件序列的,而 compose() 是针对 Observable 自身进行变换。

observable1
    .lift1()
    .lift2()
    .lift3()
    .lift4()
    .subscribe(subscriber1);
observable2
    .lift1()
    .lift2()
    .lift3()
    .lift4()
    .subscribe(subscriber2);
observable3
    .lift1()
    .lift2()
    .lift3()
    .lift4()
    .subscribe(subscriber3);
observable4
    .lift1()
    .lift2()
    .lift3()
    .lift4()
    .subscribe(subscriber1);
/**
这个时候,就应该用 compose() 来解决,可读性、可维护性都提高了
**/
public class LiftAllTransformer implements Observable.Transformer<Integer, String> {
    @Override
    public Observable<String> call(Observable<Integer> observable) {
        return observable
            .lift1()
            .lift2()
            .lift3()
            .lift4();
    }
}
...
Transformer liftAll = new LiftAllTransformer();
observable1.compose(liftAll).subscribe(subscriber1);
observable2.compose(liftAll).subscribe(subscriber2);
observable3.compose(liftAll).subscribe(subscriber3);
observable4.compose(liftAll).subscribe(subscriber4);

9. lift()源码

lift()方法很重要,针对事件序列的处理和再发送,很多类似map() flatMap()等都是通过它实现的!

讲述 lift() 的原理只是为了让你更好地了解 RxJava ,从而可以更好地使用它。RxJava 都不建议开发者自定义 Operator 来直接使用 lift(),而是建议尽量使用已有的 lift() 包装方法(如 map、flatMap 等)进行组合来实现需求,因为直接使用 lift() 非常容易发生一些难以发现的错误。

@Test
public void go_lift() {
    Observable.just(1).lift(new Observable.Operator<String, Integer>() {
        @Override
        public Subscriber<? super Integer> call(final Subscriber<? super String> subscriber) {
            // 将事件序列中的 Integer 对象转换为 String 对象
            return new Subscriber<Integer>() {
                @Override
                public void onNext(Integer integer) {
                    subscriber.onNext("x" + integer);
                }
                @Override
                public void onCompleted() {
                    subscriber.onCompleted();
                }
                @Override
                public void onError(Throwable e) {
                    subscriber.onError(e);
                }
            };
        }
    }).subscribe(x -> System.out.println(x));
}

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. 这个新 OnSubscribecall()方法中的 onSubscribe,就是指的原始 Observable 中的原始 OnSubscribe,在这个call()方法里,新 OnSubscribe利用 operator.call(subscriber) 生成了一个新的SubscriberOperator就是在这里,通过自己的 call()方法将新 Subscriber和原始 Subscriber进行关联,并插入自己的『变换』代码以实现变换),然后利用这个新 Subscriber向原始 Observable 进行订阅

两次和多次的 lift() 同理

 

lift()过程有点像一种代理机制,通过事件拦截和处理实现事件序列的变换,它是如何实现的呢?

这就是classA.this的奥秘,看下面代码吧,体会到 lift.action.call("world"); 神奇吧!

//使用 ObservableX.this时,它在内部成员变量中访问时,访问的不是当前作用域的地址
@Test
public void go_thisObj() {
    ObservableX observableX = new ObservableX(str -> System.out.println("hello"));
    observableX.display();
    ObservableX lift = observableX.lift();
    lift.display();
    lift.action.call("world");//此次是关键点(代理拦截)
}
public class ObservableX {
    Action1<String> action;

    public ObservableX(Action1 action) {
        this.action = action;
    }

    public void display() {
        System.out.println(ObservableX.this.action);
        System.out.println(action);
    }

    public ObservableX lift() {
        return new ObservableX(new Action1<String>() {
            @Override
            public void call(String s) {
                System.out.println(ObservableX.this.action);
            }
        });
    }
}
/**
com.example.rx.ReactiveX_test$$Lambda$1/863831416@39fb3ab6
com.example.rx.ReactiveX_test$$Lambda$1/863831416@39fb3ab6
com.example.rx.ReactiveX_test$ObservableX$1@6276ae34
com.example.rx.ReactiveX_test$ObservableX$1@6276ae34
com.example.rx.ReactiveX_test$$Lambda$1/863831416@39fb3ab6
**/

下面开始看源码吧

/**伪代码
 Observable 执行了 lift(Operator) 方法之后,会返回一个新的 Observable,这个新的 Observable 会像一个代理一样,负责接收原始的 Observable 发出的事件,并在处理后发送给 Subscriber
**/
public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
    return Observable.create(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber subscriber) {
            Subscriber newSubscriber = operator.call(subscriber);
            newSubscriber.onStart();
            onSubscribe.call(newSubscriber);
        }
    });
}
//源码
public class Observable<T> {
    public final <R> Observable<R> lift(final Observable.Operator<? extends R, ? super T> operator) {
        return new Observable(new Observable.OnSubscribe<R>() {
            public void call(Subscriber<? super R> o) {
                try {
                    Subscriber st = (Subscriber)Observable.hook.onLift(operator).call(o);

                    try {
                        st.onStart();
                        //这个非常抽象(onSubscribe所指代的对象不同)
                        Observable.this.onSubscribe.call(st);
                    } catch (Throwable var4) {
                        Exceptions.throwIfFatal(var4);
                        st.onError(var4);
                    }
                } catch (Throwable var5) {
                    Exceptions.throwIfFatal(var5);
                    o.onError(var5);
                }

            }
        });
    }
}

Map一般用于对原始的参数进行加工处理,concatMap是有序的,flatMap是无序的,他们底层都是通过lift 实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值