RxJava So Easy! 带你从0手撸一个RxJava

4,174 阅读11分钟

前言

本篇是源码系列第二篇,上一篇是Okhttp,下一篇计划是Retrofit

什么,还有这么简单的OkHttp源码分析?

RxJava从几年前开始流行,以其链式风格的调用和强大的操作符而闻名于世。当然,最重要的是异步切换简单到了令人发指的地步。但是,凡事都有两面性。RxJava的上手难度也是特别陡峭的,而且经常会遇到团队里其他人对于RxJava的掌握不是很熟练。

这一切的根源,都来源于它的设计思想,事件驱动型编程。

什么?看不懂?

没关系,接下来我一行源码也不会贴出来,让你自己就能手撸一个RxJava。

为什么不贴源码?

说句实话,我个人看技术文,最讨厌的就是大篇幅的贴源码,这种文章我看了只会觉得恶心。源码就在那里,我自己不会看?所以本文拒绝贴大篇大篇源码,只用最简单的代码实现RxJava。

让你以后在遇到RxJava的面试题,你直接告诉他,面试官,要不这样别问了,我给你手写一个RxJava你看可还行?

阅读本文需要有一定的RxJava基础。起码会简单的使用

观察者模式

要理解RxJava,必须要理解一个模式,就是观察者模式。

什么是观察者模式呢?

就好比搞公众号,你订阅了一个公众号,此时,你是观察者,公众号是被观察者。你订阅公众号简而言之就是你与公众号建立了一个联系,公众号更新文章后就会推送给所有订阅的人,其中也包括你。

两个角色,一个关系。

被观察者,有变化通知给订阅了的观察者。对应公众号

观察者,主动与一个被观察者建立订阅关系。对应你

那么代码上该如何实现呢?

简单实现观察者模式

创建一个观察者

class Observer {
    fun change(){
        println("我是观察者")
    }
}

创建一个被观察者

class Observable {
	//观察者集合
    private val observerList= mutableListOf<Observer>()
	//订阅方法
    fun subscribe(observer: Observer){
        observerList.add(observer)
    }
    //通知所有观察者
    fun notifyObserver(){
        observerList.forEach {
            it.change()
        }
    }
}

等等,你这被观察者怎么一堆东西?

我们一会再说这个里面的东西

fun main() {
    //创建一个被观察者
    val observable = Observable()
    //创建一个观察者
    val observer=Observer()
    //被观察者订阅观察者
    observable.subscribe(observer)
    //被观察者通知观察者事情发生了改变
    observable.notifyObserver()
}

看着观察者模式好像很深奥的样子,其实就是被观察者中有一个观察者的集合,订阅其实就是将观察者添加到这个集合中。

当有新消息的时候,被观察者遍历观察者集合,然后调用他们的方法即可。

RxJava中的观察者模式

RxJava中的观察者模式更简单,上面所说的可能会出现一堆的观察者,而RxJava模式中的观察者与被观察者是一对一的。怎么样,是不是更进一步降低了难度?

为了更好地理解事件驱动,我们可以用上游和下游来代替被观察者和观察者。

什么是上游和下游?

简单的来讲就是两个水管,这两个水管以一定的方式连接了起来,水管里的水从上面的水管流到了下面的水管。

也就是说上面的水管负责流水,下面的水管负责接水。水就是事件

我们一般是这么使用的

//创建上面的水管
Observable.create<Int> { emitter ->
   	//让水流出去                     
    emitter.onNext(10)
}	//连接两个水管	
	.subscribe (
     //创建下面的水管   
     { item ->   
      	//上面的水流下来了,打印出来
    	println(item)
     }
)

这是一般的使用方式,即创建一个上游,然后上游发送事件,下游去接收。

还有其他的使用方式,比如切换线程,或者对数据进行其他操作。

Observable.create<Int> { emitter ->
    emitter.onNext(10)
}
    .map{} //对数据进行转换
    .subscribeOn()//改变上游发送的线程
    .subscribe{}

这就是RxJava引以为傲的操作符,本质上这些操作符都是在上游和下游之间进行的一些操作。看完本篇你就明白操作符的本质,甚至可以自己写一些操作符了。

所以话不多说,我们开始彻底解剖RxJava,看看RxJava本质上到底是如何操作的。不用怕,没有源码,我只会用最简单的代码来实现一个具有完整功能的RxJava,让你彻彻底底的明白RxJava操作符,以及RxJava的线程切换。

手撸RxJava

从最基础开始走起,我们知道RxJava首先需要两个类,一个观察者,一个被观察者。

我们来定义这两个类

定义被观察者和观察者

//被观察者
class MlxObservable{}
//观察者
interface MlxObserver{}

有喜欢搞事的小伙伴可能会发现,这不对啊,你是不是骗我,你这个观察者不是类,而是个接口啊。

哎呀,行骗被发现了,赶紧逃~

其实也没有骗你,你想想,我们平常在使用RxJava的时候,是怎么使用的?Java中是不是需要new一个匿名内部类,Kotlin中则需要定义一个object实现接口。没错,观察者就是采用接口的。

为什么要这么做?

你想,观察者收到被观察者的通知以后是不是需要做一些改变,RxJava根本不知道你要做哪些改变啊,所以做成接口,你自己来实现。

又有人会问了,那你的意思被观察者也得做成一个接口啊,因为RxJava也不知道我要发送哪些数据啊。就像你上面的代码,被观察者不也是自己实现了一个接口?

恭喜你,都会抢答了!

没错,被观察者也应该做成接口,让开发者自己去实现发送哪些数据。RxJava也正是这样做的,不过为了感官上的统一,RxJava并没有把这个接口的名字叫做Observable,而是叫成了ObservableOnSubscribe

我个人更喜欢叫ObservableOnSubscribe为真正的被观察者。

定义一个真正的被观察者:

//真正的被观察者
interface MlxObservableOnSubscribe{}

被观察者也有了,观察者也有了,接下来我们需要思考下一步该做什么?

首先我们可以肯定的是,光有接口是不行的,我们得需要定义方法,来确定每个接口应该做什么。

那么我们应该思考这两个接口的职责。

从被观察者开始,被观察者应该有向下游发送数据的能力。什么叫向下游发送数据的能力呢?

其实很多人都会讲的很玄而又玄,其实就是持有下游的引用,调用下游的方法,简而言之就是回调。

定义上游的方法

不妨我们先定义这么一个方法:

interface MlxObservableOnSubscribe{
    fun setObserver(observer:MlxObserver)//设置下游
}

我们暂且不去考虑如何去将下游添加到上游去,现在开发者只需要实现了被观察者接口,那么就会有下游的引用了,就可以调用下游的方法了。

定义下游的方法

接下来该考虑,下游该有哪些方法。熟悉RxJava的小伙伴应该知道,我们在实现观察者的时候会重写四个方法:onSubscribe,onNext,onError,onComplete。好,我们今天模仿RxJava,也来定义这四个方法:

interface MlxObserver<T> {
    fun onSubscribe()
    fun onNext(item:T)
    fun onError(e:Throwable)
    fun onComplete()
}

可能有小伙伴对这个<T>不是很理解了。这个是泛型,onNext中发送的数据可能是String,也可能是Int,我们无法知道具体是哪个类型,所以我们定义一个泛型,相当于占位的,只需要调用的时候指定了是哪个类型,onNext就会收到哪个类型的数据。

既然下游定义了泛型,上游也得进行相应的修改了,需要增加泛型。

interface MlxObservableOnSubscribe<T>{
    fun setObserver(observer:MlxObserver)//设置下游
}

如此以来,实现了上游接口以后,就会得到下游的对象,调用下游的四个方法即可。其实可以看到,所谓的上游,下游其实很扯淡,就是设置了一个回调而已。也没什么难的。

现在上游也有了,下游也有了,那么我们该如何给上游设置一个下游呢?

还记得我们前面虚假的那个被观察者么,对对对,就是Observable对象。我们可以利用它来完成被观察者观察者这么一路下来的神奇操作。

RxJava中是用静态方法来完成上游的创建的,所以我们也搞个静态的。

接收上游对象

class MlxObservable{
    //静态方法创建一个真正的被观察者
	companion object{
        fun <T> create (source:MlxObservableOnSubscribe<T>):MlxObservable{
            return MlxObservable()
        }
    }
}

仔细分析一下代码,我们这里创建了一个静态方法用于接收一个真正的被观察者。返回了一个虚假的被观察者。

为什么要返回一个虚假的被观察者呢?这是因为RxJava后续的方法都不再是静态的了,所以我们需要得到一个对象。但其实更重要的原因是因为RxJava使用了装饰者模式,能够完成更好的功能拓展,各种各样的操作符也正是拓展之一。至于装饰者模式是什么,以及如何实现装饰者模式,我就不过多讲解了,看完本篇你应该会有所了解。

现在我们继续分析,我们现在返回了一个虚假的被观察者以后,需要借助这个虚假的观察者作为平台,将下游设置给上游。

接收下游对象

所以我们不妨在给虚假的被观察者也设置一个方法,这里需要注意的是我们既然返回的是类对象,那就不能在定义静态方法了。

class MlxObservable{
    ...
    //这里接收一个下游对象,
	fun setObserver(downStream: MlxObserver<T>){
        
    }
}

问题来了,下游是有了,上游呢?

哎呀呀,这是个问题,上面create方法确实是设置了一个上游,可是咱没变量保存啊。所以我们需要定义一个变量去保存上游。

private  var source:MlxObservableOnSubscribe<T>?=null

同时,最好在构造方法中就获得到上游:

class MlxObservable<T>  constructor(){
   //上游对象
   private  var source:MlxObservableOnSubscribe<T>?=null
   //次构造方法,用于接收上游对象
   constructor(source:MlxObservableOnSubscribe<T>):this(){
       this.source=source
   }
}

既然这样,我们create方法也应修改一下:

fun <T> create (emitter:MlxObservableOnSubscribe<T>):MlxObservable<T>{
    return MlxObservable(emitter)
}

连接上下游

这样以来,上游下游都有了,该怎么办?当然是盘它了!在接收下游对象的时候上游肯定已经创建了,所以我们直接在这里就可以进行上下游的连接了,其实也就是将下游设置给上游

class MlxObservable<T>{
    ...
    //这里接收一个下游对象,
	fun setObserver(downStream: MlxObserver<T>){
        source?.setObserver(downStream)
    }
}

如此以来,我们RxJava就已经完成了。

什么??已经完成了,你怕不是在骗我小猫咪。

不信?我们来跑一下

MlxObservable.create(object :MlxObservableOnSubscribe<Int>{
    override fun setObserver(emitter: MlxObserver<Int>) {
        println("上游发送数据:10")
        emitter.onNext(10)
    }
})
    .setObserver(object :MlxObserver<Int>{
        override fun onSubscribe() {
			println("onSubscribe")
        }
        override fun onNext(item: Int) {
            println("下游接收到数据:$item")
        }
        override fun onError(e: Throwable) {}
        override fun onComplete() {}
})

结果是这样的:

如何?上面的代码风格是不是已经是RxJava的风格了?不过就是变量名有点丑,人家的是subscribe方法名,我是setObserver方法名,不过不影响使用。我们后期再把它改过来,现在先这样为了能更好的理解。

有人会问,说你这个onSubscribe方法没调用啊。也是,不过这个方法见名思意就知道是在订阅的时候会调用,不管你发不发送数据,基于此,我们可以得出结论,我们应在设置之前调用下游的这个方法。也就是虚假的被观察者在收到下游之后,立马调用下游的这个onSubscribe方法:

class MlxObservable<T>{
    ...
    //这里接收一个下游对象,
	fun setObserver(downStream: MlxObserver<T>){
        downStream.onSubscribe()
        source?.setObserver(downStream)
    }
}

如此一来就可以了。最简单的RxJava即宣告完成。

下面是简单的模型

流程步骤

  1. 使用虚假的被观察者的静态方法create创建一个真正的被观察者对象,然后设置到虚假的被观察者的sourece对象中
  2. 调用虚假的被观察者的setObserver方法创建一个观察者对象,并立马调用观察者的onSubscribe方法,然后将观察者设置给被观察者
  3. 真正的被观察者调用观察者的方法。
  4. 观察者收到数据

上图中真正的被观察者有两个矩形,其实是一个对象,我只不过把它抽出来更好的表示。

可是不对啊,RxJava明明那么强大,各种操作符,线程切换你这都不行啊。

没错,接下来我们先完成一个操作符再说。emmm,哪个操作符呢?

那就用我最常用的map把,比较有代表性。

自定义Map操作符

我们继续思考,如果我们定义Map操作符,它会作用于什么地方?

显而易见的是map操作符会作用与上游和下游之间,也就是他们的中间。其实这也正是装饰者模式要做的事情,即增强对象。

我们可以采用一种方式来进行这个操作,既然它在上下游之间,我们可不可以承接上游的水,然后做了想要的变换以后,再把变换之后的水放给下游呢?

在RxJava中,create方法调用之后即可调用map方法,map方法显而易见是一个类方法,不是静态方法,所以我们先定义map方法。但我们要考虑到的是,map方法结束之后仍然可以与下游进行通信,完完全全的保持不变,所以map方法返回的必定是一个虚假的被观察者对象。

有了这些方法我们就可以写出如下代码:

class MlxObservable<T>{
	fun map():MlxObservable{}
}

但是呢,MlxObservable也就是虚假的被观察者也是有泛型的。但是它应该是T类型吗?

答案不是,为什么呢?我们首先需要知道map的作用是什么,它是转换类型的,比如说一个string类型给你转换到下游之后就变成了Int类型。所以我们不能在使用T类型了,而要转换的类型我们也不知道,所以我们再次定义一个R类型。

于是,代码变成了这样:

class MlxObservable<T>{
	fun <R> map():MlxObservable<R>{}
}

方法是有了,我们该如何做变换呢?

RxJava当然不知道你想如何转换,所以RxJava也定义了一个接口,让你去实现接口,并且根据接口的返回类型作为下游的类型。

不明白?

就是说,你需要自己去定义转换的规则,根据你return的类型就能知道R是什么类型了。Java接口太麻烦了,我们可以使用Kotlin的高阶函数来实现这一点。

在map中我们传入一个高阶函数作为转换规则:

fun <R> map(func:(T)->R):MlxObservable<R>{
    
}

func:(T)->R 就是一个高阶函数,它本质上和Java的单方法的接口是一样的。这个参数func是一个函数,函数的参数是一个T类型,返回值是R类型。就这么简单。

OK,转换规则有了,我们该如何应用转换,并将转换后的数据传给下游呢?

其实很简单,熟悉装饰者模式的小伙伴应该已经知道了,不知道的也没关系。

我们接下来要做的事情就是再定义一个map自己的真正的被观察者,用于承接上游和通知下游。不明白没关系,我们先这样做,你一会就明白了。

class MlxMapObservable <T, R>():MlxObservableOnSubscribe<R>{}

这里可能小伙伴会不明白,为什么要定义两个泛型。还记得map方法中我们又定义了一个R类型吗?为什么要定义那个R类型呢?因为已经有个T类型了,还需要一个不确定的类型,所以定义成R了。

T类型就是上游发射的类型,R类型就是要转换之后的类型。

这个类继承了MlxObservableOnSubscribe接口,这个接口就是真正的被观察者。这么做的目的是什么呢?

其实很简单,上面说了,map既然是在上下游之间,所以它既要承接上游,又要传递数据给下游。所以它自己做一个真正的被观察者,下游去观察它,它再去观察上游,上游发送数据以后,map首先收到数据,然后应用转换规则将转换后的数据在传递给下游,也就是调用下游的方法。

还不明白?

在之前的模型中,下游是直接将自己设置给了上游,而上游是直接调用下游的方法。

现在的模型中,是下游将自己设置给了map,map又将自己设置给了上游,上游依然是调用下游的方法,不过此时上游的下游不再是真正的下游了,而成为了map,当上游调用了下游的方法其实是调用了map的方法,map方法收到消息之后,应用转换,然后再次调用真正的下游。

说的太多你们可能也不大明白,我不如写出来,你们就能立马明白了。

既然MlxMapObservable实现了MlxObservableOnSubscribe接口,那么它应该是这样

class MlxMapObservable <T, R>():MlxObservableOnSubscribe<R>{

    override fun setObserver(downStream: MlxObserver<R>){
        //此时的downStream就是真正的下游
    }
}

此时我们既然有了下游,我们是不是也应该获得上游和转换规则啊。

说的真对

我们此时就来把这两个写在构造方法中:

class MlxMapObservable <T, R>(
    	private val source:MlxObservableOnSubscribe<T>, 
    	private val func:((T)->R)
	):MlxObservableOnSubscribe<R>{

    override fun setObserver(downStream: MlxObserver<R>){
        //此时的downStream就是真正的下游
    }
}

此时,我们上游source,转换规则func,下游downStream都有了。我们该做一些事情了,在前面说到,map应该将自己设置给上游,可是map是一个被观察者啊,上游接收的是一个观察者。

所以我们需要在map自己定义一个观察者,用于接收上游传下来的数据。

class MlxMapObservable <T, R>(
    	private val source:MlxObservableOnSubscribe<T>, 
    	private val func:((T)->R)
	):MlxObservableOnSubscribe<R>{

    override fun setObserver(downStream: MlxObserver<R>){
        //此时的downStream就是真正的下游
    }
    class MlxMapObserver<T,R>(
        	private val downStream:MlxObserver<R>, 
        	private val func:((T)->R)
    	):MlxObserver<T>{

        override fun onSubscribe() {
            downStream.onSubscribe()//当接收到上游传来的订阅后,将事件传递给下游
        }

        override fun onNext(item: T) {
            //应用转换规则,得到转换后的数据
            val result=func.invoke(item)
            //将转换后的数据传递给下游
            downStream.onNext(result)
        }

        override fun onError(e: Throwable) {
            //将错误传递给下游
            downStream.onError(e)
        }

        override fun onComplete() {
            //完成事件传递给下游
            downStream.onComplete()
        }

    }
}

我们定义了一个只属于map自己的观察者对象MlxMapObserver,并且在它的构造方法中,将真正的下游传给了它,在map的观察者对象中,它的所有方法将会传递给真正的下游downStream。

需要特别注意的是在onNext方法中,map方法就是在这一步应用了func的转换,将T类型的数据转换为了R类型,并将R类型的数据传递给了真正的下游downStream。

接下来的事情就很简单了,就是在setObserver方法中去承接上游,将自己的观察者对象给上游

override fun subscribe(downStream: MlxObserver<R>){
    val map=MlxMapObserver(downStream,func)//创建自己的观察者对象
    source.subscribe(map)//将自己传递给上游
}

如此map就创建完了。是不是很简单呢?

其实代码很简单,但是逻辑可能有点绕,总的来说,就是创建自己的观察者对象,然后将自己的观察者对象给上游,上游传消息给下游其实是传给了map,map在自己的观察者中在对数据进行进一步的操作之后,将操作之后的数据传递给真正的下游。

map的东西创建完了,我们继续回到map方法中,map方法中已知需要返回虚假的观察者对象,而虚假的观察者对象需要一个真正的观察者对象。map就是这个真正的观察者对象,所以我们直接new一个新的虚假的观察者,并且把上游和应用规则全部传递给map,最后将map传递给这个新的虚假的被观察者。

fun <R> map(func:(T)->R):MlxObservable<R>{
    //source就是上游真正的被观察者。
    val map=MlxMapObservable(this.source!!,func)
    return MlxObservable(map)
}

空口无凭,我们来实践一下,看是不是RxJava中的效果。

MlxObservable.create(object :MlxObservableOnSubscribe<Int>{
    override fun setObserver(emitter: MlxObserver<Int>) {
        println("上游发送数据:10")
        emitter.onNext(10)
    }
})
	.map{ item->
		"这是map操作符转换后的数据:$item"
	}
    .setObserver(object :MlxObserver<String>{
        override fun onSubscribe() {}
        override fun onNext(item: String) {
            println("下游接收到数据:$item")
        }
        override fun onError(e: Throwable) {}
        override fun onComplete() {}
})

这是结果:

怎么样,是不是一模一样啊。

可能有小伙伴没看懂,没关系,我们再看一次map的模型示意图。

也就是说,map方法之后返回了一个新的虚假的被观察者对象,这个新的虚假的被观察者对象包含的真正的被观察者是map所构造的被观察者。

也就是说map操作符构造的对象里面,既有被观察者,也有观察者,它的被观察者用于接收下游,它的观察者用于观察上游。

这样是不是很清晰易懂了呢?map操作符的原理就是这么简单,相信小伙伴可以根据map操作符自己实现其他的操作符啦~我就不再班门弄斧了。

接下来我们开始研究RxJava所谓最高深,最难的部分。也就是RxJava是如何切换线程的。

RxJava切换线程

首先RxJava切换线程是使用了两个方法,分别指定上游的线程和下游的线程。

切换上游线程具体是切换了哪个方法的线程呢?用过的小伙伴应该知道,切换的是我们构造的真正的被观察者中所实现的方法。那么这个方法是在哪里被调用的?

没错,是虚假的被观察者调用的。也就是MlxObservable的setObserver方法中调用了上游的setObserver方法。

我们现在如果要改变上游的setObserver方法所在线程,我们只能在虚假的被观察者对象中去改变它。而既然最开始创建的那个虚假的被观察者对象的方法已经写死了,所以我们可以按照map操作符的思想,我们自己去构造一个虚假的被观察者对象,在里面就像map一样承接上游,改变线程。

什么?听不明白?

map是不是承接了上游?

是的。

map怎么承接的上游?

map自己去构造了一个真正的被观察者,然后调用了上游的setObserver方法,把自己设置进去了。

你看,你也知道,map里面调用了上游的setObserver方法把。我们之前讨论了,要改变的不就是这个方法所在线程么,所以我们再定义一个类似map的操作符,然后在别的线程承接上游不就完事了么?

OK,那我们看代码如何实现把。

我们定义一个改变上游线程的操作符,既然是模仿RxJava,那我们就模仿它的方法名subscribeOn把。

改变上游线程

仿照map定义一个类,去实现真正的被观察者接口,同时为了正常的传递数据给下游,也得定义一个自己的观察者对象。

class MlxSubscribeObservable <T>(
    val source:MlxObservableOnSubscribe<T>):MlxObservableOnSubscribe<T>{

    override fun setObserver(downStream: MlxObserver<T>){
        val observer=MlxSubscribeObserver(downStream)
    }

    class MlxSubscribeObserver<T>(val downStream:MlxObserver<T>):MlxObserver<T>{
        ...
    }
}

再仿照map定义一个成员方法:

fun subscribeOn():MlxObservable<T>{
    val subscribe=MlxSubscribeObservable(this.source!!)
    return MlxObservable(subscribe)
}

好了,一切都没问题了。可是这也没有改变上游线程啊,甚至setObserver方法中你都没有承接上游呢。

是这样的,我们需要思考线程的创建与销毁是十分消耗性能的。我们不能单纯的new一个Thread出来,那样性能开销十分大,所以针对线程问题,我们需要线程复用,而线程复用首先应该能想到我们要使用线程池。RxJava也正是这样做的。

那么问题来了,RxJava切换线程的时候可是有好几种线程呢,什么IO,什么computation的,还有Android主线程呢。

其实啊,除了Android主线程以外,其他的都是线程池里面抽出来的,区别只是线程池的策略不同而已。比如IO使用的是CachedThreadScheduler线程池,这个线程池是带缓存的,也就是说可以复用的线程池。简单这么理解就好,线程池和线程同步等等问题,我随后专门开出一个系列来讲,可以关注一下我到时候收看。

所以针对各种各样不同的线程操作,我们需要一个类来专门管理这些线程。RxJava也是这么做的,它取的名字叫Schedulers,所以我们也起这个名字

定义线程池管理类

class Schedulers(){}

光有这么一个类可不行,我们得管理线程池,既然要管理线程池那最起码得有线程池把。所以我们在这个里面定义一个线程池对象,线程池那么多,我们就只定义一个IO线程池把。

class Schedulers(){
	private var IOThreadPool =Executors.newCachedThreadPool()//IO线程池
}

主线程??一会搞一会搞,你别急嘛

IO线程池来了,RxJava好像是静态方法,你这个是类的方法,它不一样啊。

嗨,RxJava那个只是制定策略而已,我们也可以搞个类似的,不过就不模仿RxJava那么复杂了,就简单的用Int来表示策略把。

class Schedulers(){
	private var IOThreadPool =Executors.newCachedThreadPool()//IO线程池
    companion object{
        //定义一个线程安全的单例模式
        private val INSTANCE: Schedulers by 
        	lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            	Schedulers()
        }
        private val IO=0 //定义IO线程策略
        private val MAIN=1 //定义main线程策略
    }
}

我们定义了一个线程安全的单例模式,至于怎么实现的,感兴趣的小伙伴可以自己去看下这个。本质上还是双重校验那一套。

万事俱备,只欠东风了。

既然都有了,我们可以搞一下任务了。

什么任务?

当然是切换线程的任务了,我们要在指定线程切换任务,所以我们需要定义一个方法来切换线程。既然切换线程,也就是说指定线程里运行上游的setObserver传入一个下游,你看看,参数不就出来了,要能切换,首先得有一个上游,其次得有一个下游,然后得指定线程。

好,定义这么一个方法:

fun <T> submitSubscribeWork(
	source: MlxObservableOnSubscribe<T>, //上游
	downStream: MlxObserver<T>,//下游
	thread:Int//指定的线程
	) {
        when(thread){
            IO->{
                IOThreadPool.submit {
                    //从线程池抽取一个线程执行上游和下游的连接操作
                    source.setObserver(downStream)
                }
            }
            MAIN->{
                
            }
        }

}

代码看起来很简单。就是得到上下游之后,在线程池里进行了上下游的连接操作,也就是把下游设置给了上游。我们留下一个main,因为main还没实现,我们一会在实现。现在这个线程池也有了,方法也有了,我们回到切换线程的操作符里把这个切换线程的任务提交给线程池管理类。

class MlxSubscribeObservable <T>(
    private val source:MlxObservableOnSubscribe<T>,
    private val thread:Int //看这里,新增了指定线程
	):MlxObservableOnSubscribe<T>{

    override fun setObserver(downStream: MlxObserver<T>){
        val observer=MlxSubscribeObserver(downStream)
        //提交任务给指定线程,也就是再指定线程完成上下游的链接
        Schedulers.INSTANCE.submitSubscribeWork(source,downStream,thread)
    }

    class MlxSubscribeObserver<T>(val downStream:MlxObserver<T>):MlxObserver<T>{
        ...
    }
}

那么问题又来了,不能光别的线程啊,万一我就想主线程发送怎么办?

所以我们来搞主线程的东西。

说起主线程,我们不得不提到一个东西,没错,就是Handler。如果你对Handler还不了解的话, 建议看看我的文章看完这篇还不明白Handler你砍我,很多人说我这篇文章是讲Handler最好的文章~嘻嘻嘻,没错很多人就是我。

所以我们再Android平台切换线程,永远绕不过去的一个点就是Handler。协程?协程在Android上也是封装了Handler,想不到把?随后我也可以带你手动实现一个协程。今天就不多说了。

所以我们需要定义一个Handler,并且因为是主线程,所以我们需要构造一个主线程下的Handler:

class Schedulers(){
    ...
    private var handler=Handler(Looper.getMainLooper()){ message->
        //这里就是主线程了。                                                
        return@Handler true
    }
}

我们继续思考,我们收到切换线程的任务之后,应该如何去设置上下游的代码传给handler呢?答案就是message的callback。

fun <T> submitSubscribeWork(
    source: MlxObservableOnSubscribe<T>, 
    downStream: MlxObserver<T>,
    thread:Int) 
{
    when(thread){
        IO->{
            ...
        }
        MAIN->{
            val message=Message.obtain(it){
                	//上下游的连接
                    source.subscribe(downStream)
                }
            it.sendMessage(message)
        }
    }
}

其实是个runnable的接口,在这里实现了上下游的链接,handler收到消息之后,只需要这样即可实现切换到主线程:

class Schedulers(){
    ...
    private var handler=Handler(Looper.getMainLooper()){ message->
        //这里就是主线程了。
 		message.callback.run()
        return@Handler true
    }
}

OK了,那我们来实践一下,看看效果是不是真的如我们预期的一样把?

MlxObservable.create(MlxObservableOnSubscribeJava<Int> {
    Log.i("zzz", "上游线程:${Thread.currentThread().name}")
    it.onNext(10)
})      
    .subscribeOn(Schedulers.IO())
    .subscribe(object : MlxObserver<Int> {
        override fun onNext(item: Int) {
            Log.i("zzz", "下游线程:${Thread.currentThread().name}")
        }
        ...
})

OK 完美。成功切换上游线程

不过下游线程怎么也跟着切换了?嗨,RxJava也是这样操作的,因为你没有指定下游的线程,所以就默认上游和下游是一个线程的呀~

那我们依葫芦画瓢,按照上面的切换上游线程也写个切换下游线程的真实的被观察者和观察者,以及方法。

改变下游线程

首先是下游的被观察者对象:

class MlxObserverObservable<T>(
        private val source: MlxObservableOnSubscribe<T>, 
        private val thread: Int
	) :MlxObservableOnSubscribe<T> {
        
    override fun setObserver(downStream: MlxObserver<T>) {
        val observer = MlxObserverObserver(downStream, thread)
        source.subscribe(observer)
    }
    class MlxObserverObserver<T>(
            val downStream: MlxObserver<T>, 
            val thread: Int
    	) : MlxObserver<T> {

        override fun onSubscribe() {
            Schedulers.INSTANCE.submitObserverWork({
                downStream.onSubscribe()
            }, thread)
        }

        override fun onNext(item: T) {
            Schedulers.INSTANCE.submitObserverWork({
                downStream.onNext(item)
            }, thread)
        }

        override fun onError(e: Throwable) {
            Schedulers.INSTANCE.submitObserverWork({
                downStream.onError(e)
            }, thread)
        }

        override fun onComplete() {
            Schedulers.INSTANCE.submitObserverWork({
                downStream.onComplete()
            }, thread)
        }
    }
}

忽略我垃圾的命名把。和改变上游线程不同的是,改变下游线程不需要拿到上游对象,所以定义的这个submitObserverWork方法只需要两个参数即可,一个是下游,一个是指定的线程。

我们来看一下这个方法,其实和上面的方法一模一样:

class Schedulers{
    fun  submitObserverWork(function: () -> Unit,thread:Int) {
        when(thread){
            IO->{
                IOThreadPool?.submit {
                    function.invoke() //调用高阶函数
                }
            }
            MAIN->{
                handler?.let {
                    val m=Message.obtain(it){
                        function.invoke()//调用高阶函数
                    }
                    it.sendMessage(m)
                }
            }
        }
    }
}

唯一不同的就是function这里使用的是高阶函数,并不是接口而已。

OK,话不多是,实践起来,看看有没有效果才是硬道理。这里我偷偷的把setObserver改成了subscribe名字,毕竟看起来能高大上一点嘛~~

MlxObservable.create(MlxObservableOnSubscribeJava<Int> {
    Log.i("zzz", "上游线程:${Thread.currentThread().name}")
    it.onNext(10)
})      
    .subscribeOn(Schedulers.IO())
	.observerOn(Schedulers.mainThread())
    .subscribe(object : MlxObserver<Int> {
        override fun onNext(item: Int) {
            Log.i("zzz", "下游线程:${Thread.currentThread().name}")
        }
        ...
})

怎么样!!!

是不是成功的切换了上下游的线程呀~~~完美。

看不大明白?

上图,你就明白了

RxJava线程的一些问题

所以可以看到,如果有多个设置上游线程的虚假的被观察者对象的话,也就是说如果多次调用设置上游线程,那么只有第一个会生效。其他的虽然也改变了线程,但是下游并不知道。

为什么?

因为假如设置了3次,1,2,3.那么调用顺序是这样:3会先设置一次线程,2会改变一次,1会改变一次。所以只有1能生效。

而下游的话改变几次,最后一个才会生效。

但是这是针对有多个下游的情况,改变几次生效几次。

什么是多个下游?嗨,就是doOnNext了

如果这么写就是多个下游:

.observerOn(Schedulers.mainThread())
.doOnNext {  } //一个下游
.observerOn(Schedulers.IO())
.doOnNext {  }//第二个下游
.observerOn(Schedulers.mainThread())

doOnNext操作符也很简单,大家也可以自己实现以下。如果没头绪,也可以看看我的实现方式。我只实现了几个有限的操作符,其他的时间不够了。

这是本篇地址:大家感兴趣的话可以看看:

MlxRxJava

欢迎关注我,喜欢自定义View,钻研Android技术的小喵喵~