文章目录
这里以RxJava3的源码来讲解,不再考虑旧版本代码中的类关系
依赖引入
api("io.reactivex.rxjava3:rxjava:3+")
基本用法展示
这里我们故意通过Emitter的方式,来展示RxJava中的所有核心类
在实际应用中,我们可能不会这么去做,因为RxJava为我们封装了很多更简单的操作符
但如果我们想弄懂底层原理,还是从底层代码入手比较好
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.ObservableEmitter
import io.reactivex.rxjava3.core.ObservableOnSubscribe
import io.reactivex.rxjava3.core.Observer
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.functions.Consumer
fun main() {
val observableOnSubscribe = ObservableOnSubscribe { emitter: ObservableEmitter<String> ->
try {
if (!emitter.isDisposed) {
emitter.onNext("${emitter.hashCode()} Event 1")
emitter.onNext("${emitter.hashCode()} Event 2")
emitter.onNext("${emitter.hashCode()} Event 3")
emitter.onComplete()
}
} catch (e: Exception) {
emitter.onError(e)
}
}
val observable = Observable.create(observableOnSubscribe)
.map { it }
val observer1 = createObserver()
val observer2 = createObserver()
val consumer = createConsumer()
observable.subscribe(observer1)
observable.subscribe(observer2)
observable.subscribe(consumer)
}
fun createObserver(): Observer<String> {
return object : Observer<String> {
override fun onSubscribe(d: Disposable) {
}
override fun onNext(s: String) {
System.err.println(s)
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
}
}
}
fun createConsumer(): Consumer<String> {
return Consumer { s: String ->
System.err.println(s)
}
}
类角色分析
针对以上代码,我们来看下,每个角色分别起到什么样的作用
-
Observable
被观察的对象,实现自接口ObservableSource,用来管理可发布的数据 -
Observer
观察者,可以接受Observable发布的数据 -
ObservableEmitter
数据发射器,细化Observable数据发布规则以上只是一个简单的示例,在被订阅后,立刻发送所有数据
实际业务中往往并非如此,所以需要一个专门的Emitter来决定如何发布数据
-
ObservableOnSubscribe
命名不是很准确,负责在Observable被订阅时,生成Emitter如果有多个Observer订阅,对应每个Observer都会生成一个Emitter
-
Disposable
Observable会处理一些后台工作,Disposable就是用来包装这些后台资源的
Disposable的生成工作一般是由库去做的
如果想自定义的话,可以通过ObservableEmitter.setDisposable来实现
Observer成功订阅Observable时,会返回对应的Disposable
通过Disposable.dispose方法,可以销毁后台资源,订阅关系也会随之解除
通过CompositeDisposable可以统一销毁多个Disposable
-
Consumer
Consumer是Observer的简化版本,它只有一个accept(T data)方法
当我们不想处理Observer的完整回调,只想处理onNext或onError中的一个,可以使用Consumer
-
Cancellable
和Disposable功能完全一致,只是接口名称不一样,强调的含义不一样
Cancellable强调取消任务,Disposable强调销毁资源,不涉及资源的任务,可以实现自Cancellable接口
Cancellable同样有一个ObservableEmitter.setCancellable方法,用来自定义后台任务
Cancellable也是通过Disposable.dispose方法来取消任务
因为setCancellable方法,实质上只是生成了一个CancellableDisposable(cancellable)来包装Cancellable
-
Subject
既是Observable,也是Observer
它可以被其它Observer订阅,同时又能够通过onNext发布数据,是取代EventBus和Callback的一个好方案
它有四个子类,数据发布规则各不相同,这里不细述
-
Others
网上可能教程还涉及一些其它的类,属于RxJava1和RxJava2中的实现方式,RxJava3中已经不再适用了
Observable到Observer的完整流程
- Observable创建
- Observer订阅Observable
- Observable创建Emitter,并将Observer传给Emitter
- Observable调用Emitter.onSubscribe
- Emitter调用Observer.onSubscribe
- Observable调用Emitter.setDisposable
- Disposable执行后台任务,生成数据
- Observable调用Emitter.onNext发布数据
- Emitter调用Observer.onNext转发数据给Observer
- Observable也可以直接调用Observer.onNext,Emitter不是必需的,需要自定义发布逻辑时才会使用
- 不同操作符创建的Observable,根本区别在于Disposable提供数据的逻辑不同
- Rx框架为我们封装大量常用的操作符,能够自动发布数据,转换数据,切换线程,大多时候不需要我们去自定义Emitter
线程切换
RxJava的业务代码一般会在以下时机执行:onSubscribe,onNext,onError,onComplete
其中,onNext,onError,onComplete代码运行在相同线程中
onSubscribe运行在数据订阅线程,onNext等运行在数据发布线程
subscribe线程可以通过SubscribeOn操作符来指定
publish线程可以通过ObserveOn操作符来指定
SubscribeOn对其之前的所有Observable有效
ObserveOn对其之后的所有Observable有效
当存在多个Observable串联时,订阅从下而上,发布从上而下,依次执行
Disposable是如何工作的
- Disposable是一个接口,它只定义了两个方法,dispose和isDisposed
- 可以理解为销毁资源,判断资源是否销毁。或者理解为停止工作,判断工作是否停止
- 几乎所有操作符的核心功能,都是由Disposable接口来完成的
- 这个接口一般配合Runnable接口和线程池来使用,它的子类一般会实现Runnable接口,然后再内部维护一个线程池
- Disposable的启动代码往往是:executor.submit(runnable)
- Disposable的停止代码往往是:executor.shutdown()
- 如果不存在线程调度,则通过runnable.run来直接启动工作
subscribeOn是如何工作的
- subscribeOn用法可以简单描述为Observable.subscribeOn(Scheduler).subscribe(Observer)
- subscribeOn创建了一个ObservableSubscribeOn,它本身也是一种Observable
- 所以Observer实际订阅的是ObservableSubscribeOn
- 当Observer订阅ObservableSubscribeOn时,会生成一个SubscribeOnObserver
- SubscribeOnObserver既是Observer,也是Disposable
- SubscribeOnObserver一方面以Observer到身份来订阅上游Observable,一方面以Disposable的身份来处理指定线程的订阅工作
- ObservableSubscribeOn调用Observer.onSubscribe(SubscribeOnObserver),执行Observer回调
- ObservableSubscribeOn通过scheduler.scheduleDirect(SubscribeTask)生成Disposable,显然这个Disposable是用来完成线程切换和订阅上游Observable工作的
- scheduleDirect是Scheduler的核心方法,它的核心工作就是通过内部的线程池去执行Runnable工作,这里的SubscribeTask其实就是一个Runnable
- SubscribeTask做的工作很简单,只有一行代码,就是通过SubscribeOnObserver去订阅Observable,即Observable.subscribe(SubscribeOnObserver)
- 由以上流程,我们可以得出结论,subscribeOn操作符可以决定其前一个Observable被订阅的线程,并且Observer.onSubscribe方法会先于Observable.onSubscribe方法执行
doOnSubscribe是如何工作的
- 假设我们有这样一段代码Observable.just().doOnSubscribe(Consumer).subscribeOn(Scheduler)
- doOnSubscribe为Observable设置回调,同时它自己也是一个Observable,这是RxJava的设计哲学。现在我们把doOnSubscribe简称为oa2,原始Observable简称为oa1,subscribeOn订阅的oa2。那么,为什么subscribeOn可以指定oa1被订阅的线程,这就是我们这一节要分析的
- doOnSubscribe会生成一个ObservableDoOnLifecycle,所以实际调用代码是ObservableDoOnLifecycle.subscribe(SubscribeOnObserver),这个工作是在SubscribeTask里完成的。如果Observable也想在该线程类被订阅,那么Observable的订阅工作,也一定是发生在这个subscribe方法内部。下面我们来看下实际的代码,看看ObservableDoOnLifecycle.subscribe究竟做了什么
- 通过源码我们发现实际的实现代码为,ObservableJust.subscribe(DisposableLambdaObserver(SubscribeOnObserver, onSubscribe, onDispose)),这和subscribeOn的逻辑完全一致,通过生成一个中间Observer,来达到承上启下的连接效果
- 到此为止,我们已经基本可以看出来Observable操作符的一般实现逻辑。操作符生成一个NewObservable作为返回值,NewObservable内部创建一个MediatorObserver,MediatorObserver同时实现Disposable接口,它既负责订阅OldObservable,同时又负责实现操作符的数据转换和线程切换功能。我们再次对NewObservable进行操作符运算,会再次重复以上过程,这样形成一个循环的链式调用
- 这里还有一个问题,由于ObservableJust.subscribe是在ObservableDoOnLifecycle.subscribe内部完成的,而ObservableDoOnLifecycle.subscribe又是通过SubscribeTask在指定Scheduler中完成的,所以,ObservableJust.subscribe和doOnSubscribe.Consumer也都是在该Scheduler中完成的。可以得出一个结论,Observable订阅线程和doOnSubscribe回调线程,由其后最近的一个subscribeOn操作符决定
- 如果Observable其后没有使用subscribeOn操作符,那么订阅工作将在初始Observable的创建线程执行
多次变换过程中Observable的订阅顺序
-
由于每次操作符运算,都会生成新的Observable+Observer+Disposable,那么一定会产生多次订阅工作,这里就涉及一个订阅顺序的问题
-
一个是Observable的订阅顺序,一个是doOnSubscribe的执行顺序,Observable的订阅顺序上面已经得出结论了,是从后往前执行的。doOnSubscribe的执行顺序则需要我们再次去窥探下Rx的源码,因为被订阅并不代表回调就要立刻被执行
-
不同操作符的原理大致相同,这里我们以ObservableDoOnLifecycle的源码来进行分析
fun ObservableDoOnLifecycle::subscribeActual(observer:Observer){ val source:ObservableSource source.subscribe(DisposableLambdaObserver(observer, onSubscribe, onDispose)) } fun DisposableLambdaObserver::onSubscribe(d:Disposable){ val downstream:Observer onSubscribe.accept(d) downstream.onSubscribe(this) } ``` 可以看出,subscribe是由当前Observable向上层SourceObservable一层层调用 当Observable不再有上层Observable时,开始执行Observer.onSubscribe 如果Observer有下游DownstreamObserver,则向下一层层调用onSubscribe方法
-
所以在没有线程切换的情况下,doOnSubscribe是从前到后按顺序执行的
-
当有线程切换时,即存在subscribeOn操作符时,可以通过ObservableSubscribeOn的源码来进行分析
fun ObservableSubscribeOn::subscribeActual(observer:Observer) { val parent = SubscribeOnObserver(observer) observer.onSubscribe(parent) parent.setDisposable(scheduler.scheduleDirect(SubscribeTask(parent))) }
可以看出,先执行SubscribeOn.Observer的onSubscribe方法,再通过线程池去订阅上游的Observable
当存在多个subscribeOn操作符时,会先执行subscribeOn之后的doOnSubscribe回调方法,再从下游到上游依次执行不同调度器中的doOnSubscribe回调方法
如果某个调度器中存在多个doOnSubscribe回调,则仍然按照从前往后的顺序执行
observeOn是如何工作的
-
假设,我们有这样一段代码:Observable.just().doOnNext().subscribeOn().observeOn().map().doOnNext()
-
首先,通过断点的方式,来跟踪onNext的执行顺序,可以看到的,代码的大致流程如下
=> JustObservable.subscribeActual
=> JustObserver.onSubscribe
=> JustDisposable.run
=> DoOnEachObserver.onNext
=> DoOnEachObserver.onNextConsumer.accept
=> SubscribeOnObserver.onNext
=> ObserveOnObserver.onNext
=> ObserveOnObserver.schedule
=> SchedulerWorker.schedule(ObserveOnObserver)
-
到这里为止,我们可以看到,onNext方法是从上往下依次执行的。到了ObserveOnObserver这里,开始通过SchedulerWorker来进行切换。ObserveOnObserver本身实现了Disposable和Runnable接口,它的核心工作在于run方法里面
-
接着来看ObservableObserveOn的源码
fun ObservableObserveOn::subscribeActual(observer:Observer){ val source:Observable val worker = scheduler.createWorker() source.subscribe(ObserveOnObserver(observer,worker)) } fun ObserveOnObserver::onSubscribe(d:Disposable){ val downstream:Observer downstream.onSubscribe(this) } fun ObserveOnObserver::onNext(t:T){ val worker:Scheduler.Worker worker.schedule(this) } fun ObserveOnObserver::run(){ val queue:SimpleQueue val downstream:Observer val t = queue.poll() downstream.onNext(t) }
可以看到,onNext仍然会继续向下传递,但是线程已经通过Scheduler.Worker进行切换了
-
onNext会一直向下传递,只要指定了一次ObserveOn,后面的onNext都会在该线程中执行
-
当没有指定ObserveOn,但是指定了SubscribeOn时,由于onNext代码是从最上游的Observable.subscribeActual开始的,所以所有的onNext都会在最近一个SubscribeOn指定的线程中执行
-
onError和onComplete代码运行在和onNext相同的线程里
Observable操作符
-
create:通过自定义的Emitter创建Observable,一般Emitter会和一些后台工作绑定,不是RxJava的主流使用方式
-
empty:Observable被订阅后直接发送onComplete事件,适用于发布任务完成通知
-
error:Observable被订阅后直接发送onErrore事件,适用于发布任务失败通知
-
just/fromArray/fromIterable:Observable被订阅后,连续发送数组或集合中的数据
-
buffer:将上游Observable发送的数据收集到一定数量,再以集合形式发布
-
collect:将上游Observable发送的数据收集到一起,一次性发布
-
reduce:将上游Observable的相邻两个数据进行运算,再拿结果与下一个数据进行运算,如此循环,得到一个最终值。这种运算方式用专业术语来说,叫做递归降次
-
zip:将两个Observable中的数据进行运算,得到一个新的值后再发布。如果两个Observable中的数据数量不相等,多余的数据将不会参与运算
-
concat:串行发送多个Observable中的数据
-
merge:并行发送多个Observable中的数据
-
concat和merge,只有当SourceObservable在不同线程发布数据时,才能看出效果差异。如果只有一个线程,那么不存在并发一说
-
map:将上游Observable发布的数据,转换成新的数据,并发布到下游
-
flatMap:将上游Observable发布的数据,转换成新的Observable,并发布到下游
-
map将T转为R,flatMap则将T转为Observable<R>
-
map从头到尾都是由同一个Observable发布转换后的数据,flatMap则是每次都生成一个新的Observable来发布数据
-
concatMap:和flatMap功能一样,但concatMap是串行处理转换工作,而flatMap是并行处理转换工作
-
flatMap和concatMap,也是只有当FlatObservable在不同线程发布数据时,才能看出效果差异。如果只有一个线程,那么不存在并发一说
-
大多时候,concatMap之所以是串行的,是因为它会等到上个MapperObservable执行OnComplete,才会处理下个SourceEvent
-
如果某个MapperObservable忘记调用OnComplete,concatMap可能执行一半就进入阻塞等待状态
-
想要对比flatMap和concatMap的效果,可以参考以下示例代码
-
代码示例
// buffer Observable.just(1, 1, 1, 1, 1) .buffer(2) .subscribe { list -> System.err.println(list) } // collect Observable.just(1, 2, 3, 4, 5) .collect( Supplier { mutableListOf<Int>() }, BiConsumer { collect, item -> collect.add(item) } ) .subscribe { collect -> System.err.println(collect) } // reduce Observable.just(1, 1, 1, 1, 1) .reduce { item1, item2 -> item1 + item2 } .subscribe { sum -> System.err.println(sum) } // zip Observable.zip( Observable.fromArray(1, 2, 3, 4, 5), Observable.fromArray("A", "B", "C"), BiFunction { int, str -> str + int } ).subscribe { zip -> System.err.println(zip) } // concat/merge Observable.concat( Observable.fromArray(1, 2, 3, 4, 5), Observable.fromArray("A", "B", "C"), Observable.fromArray(true, false) ).subscribe { item -> System.err.println(item) } // concatMap/flatMap Observable.just(1, 2, 3, 4, 5) .concatMap { val subject = PublishSubject.create<Int>() Thread { val delay = Random.Default.nextLong(1000L, 1500L) Thread.sleep(delay) subject.onNext(it) subject.onComplete() }.start() subject.doOnComplete { System.err.print("Complete") } } .doOnNext { System.err.print(it) } .blockingSubscribe()
map操作符做了什么
- map操作符生成了一个ObservableMap对象,它是Observable的一个子类
- ObservableMap保存了sourceObservable和mapperFunction
- ObservableMap在被下游Observer订阅时会,会先生成一个MapObserver,来订阅上游Observable
- MapObserver会保存mapperFunction和downstreamObserver
- 当sourceObservable有数据发布时,会调用MapObserver.onNext
- MapObserver通过mapperFunction进行数据转换后,再调用downstreamObserver.onNext转发到下游Observer
flatMap和concatMap在实现上的区别
- flatMap生成一个MergeObserver作为中间订阅者
- concatMap生成SerializedObserver+SourceObserver作为中间订阅者
- MergeObserver允许保存多个source和observers
- SourceObserver具备和MergeObserver类似的功能,但是它还维护了一个名为active的Bool型变量
- 当有ConcatObservable被订阅时,active置为true,ConcatObservable完成时,active置为false
- active为true的期间,其他ConcatObservable的onNext事件是不允许被处理的,因此实现了串行执行的效果
- SerializedObserver是用来保证线程安全的,保证所有onNext,onError和onComplete方法都执行在同一线程
RxJava在业务应用中的价值
- 上面我们讲的全是如何对数据进行操作,实际上,数据即是业务
- 所有的业务最终都以数据的形式存在于代码中,数据操作即是业务操作
- 比如登录操作,可以描述为username => UserInfo,这个可以通过map来实现。map通过网络请求来返回UserInfo
- 比如展示用户资料,可以描述为BasicInfo + CompanyInfo + EducationInfo => UserProfile,这个可以通过merge来实现。通过三个Observable并行获取不同的Info,再通过merge合并到UserProfile
- 由此可见,RxJava对数据和并发的支持有多灵活,在业务中的价值同样就有多大
RxJava3与Retrofit2组合使用
这里我们以网络请求为例,演示RxJava的应用场景
假设我们要获取用户信息,包括两部分,账号信息和会员信息,两个请求都成功才算获取成功
实现代码如下
dependencies {
api("com.google.code.gson:gson:+")
api("io.reactivex.rxjava3:rxjava:+")
api("io.reactivex:rxandroid:+")
api("com.squareup.okhttp3:okhttp:+")
api("com.squareup.okio:okio:+")
api("com.squareup.retrofit2:retrofit:+")
api("com.squareup.retrofit2:adapter-rxjava3:+")
api("com.squareup.retrofit2:converter-gson:+")
}
interface BaiduService {
@GET("account/{uid}")
fun loadAccountInfo(
@Path("uid") uid: String
): Observable<String>
@GET("vip/{uid}")
fun loadVipInfo(
@Path("uid") uid: String
): Observable<String>
}
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava3CallAdapterFactory.createSynchronous())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://www.baidu.com/")
.build()
val service: BaiduService = retrofit.create(BaiduService::class.java)
val loadAccountInfo: Observable<String> = service.loadAccountInfo("LiNing")
val loadVipInfo: Observable<String> = service.loadVipInfo("LiNing")
val loadUserInfo: Observable<List<String>> = Observable.zip(
loadAccountInfo,
loadVipInfo,
BiFunction { account, vip -> listOf(account, vip) }
)
loadUserInfo.doOnNext { user ->
System.err.println(user[0])
System.err.println(user[1])
}.doOnError { e ->
System.err.println(e.message)
}.subscribe()