【RxJava】RxJava3核心功能讲解和源码分析


这里以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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值