今天用到了 RxJava2 中 zip 操作符,目的是下载两张图片后合成为一张图片。具体代码不就贴出来了,简单提供一个小的操作流
模拟两个下载图片的 Flowable ,代码如下 (kotlin)
val just = Flowable.create<String>({
XLog.e("是否是主线程 1 : ${Looper.getMainLooper() == Looper.myLooper()}")
Thread.sleep(500)
it.onNext("1")
}, BackpressureStrategy.BUFFER)
// .delay(500, TimeUnit.SECONDS)
val a = Flowable.create<String>({
Thread.sleep(1000)
XLog.e("是否是主线程 2 : ${Looper.getMainLooper() == Looper.myLooper()}")
it.onNext("a")
}, BackpressureStrategy.BUFFER)
这里计划用 delay 进行延迟发送,但与 zip 操作符不能相结合。下面贴出简单的 zip
Flowable.zip<String, String, String>(
just,
a,
BiFunction { s, s2 ->
XLog.e("是否是主线程 3 : ${Looper.getMainLooper() == Looper.myLooper()}")
XLog.e("time2 " + (System.currentTimeMillis() - l))
s + s2
})
.execute(this)
.subscribe { s ->
XLog.e("是否是主线程 4 : ${Looper.getMainLooper() == Looper.myLooper()}")
XLog.e("time2 $s")
}
这里的 l 对象为 val l = System.currentTimeMillis()
预想的结果是达到了,但是查看 time2 的时间时,有如下 log
E/Base_XLog: 是否是主线程 2 : false
E/Base_XLog: 是否是主线程 3 : false
E/Base_XLog: time2 1505
E/Base_XLog: 是否是主线程 4 : true
E/Base_XLog: time2 1a
整个过程的运行时间为 1505 ,也就是说是 just 和 a 两个串行运行的时间,由此可以了解到 zip 操作符为串行将 传入的 Publisher 依次运行,查看源码 subscribeActual 可以看出,确实是 for 循环,一次执行 Publisher ,最后再将结果发射出去。(不太了解源码的可以查看我以前的文章,subscribeActual 是一个重要的函数)
public void subscribeActual(Subscriber<? super R> s) {
Publisher<? extends T>[] sources = this.sources;
int count = 0;
if (sources == null) {
sources = new Publisher[8];
for (Publisher<? extends T> p : sourcesIterable) {
if (count == sources.length) {
Publisher<? extends T>[] b = new Publisher[count + (count >> 2)];
System.arraycopy(sources, 0, b, 0, count);
sources = b;
}
sources[count++] = p;
}
} else {
count = sources.length;
}
if (count == 0) {
EmptySubscription.complete(s);
return;
}
ZipCoordinator<T, R> coordinator = new ZipCoordinator<T, R>(s, zipper, count, bufferSize, delayError);
s.onSubscribe(coordinator);
coordinator.subscribe(sources, count);
}
这样操作倒是简单了许多,但是时间上消耗的就多了。
由此,我想到了 RxJava2 有一个 toList 操作符,可以收集原始 Flowable 等发射的所有数据到一个列表,然后返回这个列表。这样岂不是可以并发运行了,如此就有如下代码
val l = System.currentTimeMillis()
Flowable.fromIterable(mutableListOf("1", "a"))
.map {
return@map it
}
.flatMap { s ->
return@flatMap Flowable.just(s).observeOn(Schedulers.io())
.map {
Thread.sleep(2000)
return@map "request done:$s"
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.toList()
.toFlowable()
.map { list ->
var s = ""
list.forEach {
s += it
}
return@map s
}
.subscribe {
XLog.e(it)
XLog.e(" time2 " + (System.currentTimeMillis() - l))
XLog.e("是否是主线程 4 : ${Looper.getMainLooper() == Looper.myLooper()}")
}
日志如下
E/Base_XLog: request : 1 request : a
E/Base_XLog: time2 2014
E/Base_XLog: 是否是主线程 4 : true
这里可以看到,运行时间为 2014 ,完美符合预期。
RxJava2 的操作符实在是多,有些我们几乎见过,所以需要对整体有个认识,主要的有如下几种:
创建操作:just()、create()、fromArray()、defer()、interval()、range() 等
变换操作符:buffer()、map()、flatMap()、concatMap()、groupBy() 等
过滤操作符列表:filter()、skip()、take()、elementAt() 即它们的一些扩展等
聚合操作符:concat()、count()、reduce()、toList()、toMap() 等
连接操作符:ConnectableObservable.connect()、Observable.publish()、Observable.replay()、ConnectableObservable.refCount() 等
还有一些运算、比较、错误处理,也包含一些 do 开头的运算符。
不需要全部都记下来,大致有个印象,使用的时候可以查看源码或文档即可。
笔者认为 RxJava 早已到了 Android 开发的顶峰了,一点不了解的有些说不过去了,因此赶快抓紧了解其 API,更要阅读其源码,了解其机制才是。