从0开始手撸一个RxJava响应式编程框架,2024年最新阿里面试hrg

interface MlxObservableOnSubscribe{

fun setObserver(observer:MlxObserver)//设置下游

}

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

定义下游的方法

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

interface MlxObserver {

fun onSubscribe()

fun onNext(item:T)

fun onError(e:Throwable)

fun onComplete()

}

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

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

interface MlxObservableOnSubscribe{

fun setObserver(observer:MlxObserver)//设置下游

}

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

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

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

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

接收上游对象

class MlxObservable{

//静态方法创建一个真正的被观察者

companion object{

fun create (source:MlxObservableOnSubscribe):MlxObservable{

return MlxObservable()

}

}

}

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

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

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

接收下游对象

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

class MlxObservable{

//这里接收一个下游对象,

fun setObserver(downStream: MlxObserver){

}

}

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

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

private var source:MlxObservableOnSubscribe?=null

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

class MlxObservable constructor(){

//上游对象

private var source:MlxObservableOnSubscribe?=null

//次构造方法,用于接收上游对象

constructor(source:MlxObservableOnSubscribe):this(){

this.source=source

}

}

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

fun create (emitter:MlxObservableOnSubscribe):MlxObservable{

return MlxObservable(emitter)

}

连接上下游

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

class MlxObservable{

//这里接收一个下游对象,

fun setObserver(downStream: MlxObserver){

source?.setObserver(downStream)

}

}

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

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

不信?我们来跑一下

MlxObservable.create(object :MlxObservableOnSubscribe{

override fun setObserver(emitter: MlxObserver) {

println(“上游发送数据:10”)

emitter.onNext(10)

}

})

.setObserver(object :MlxObserver{

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{

//这里接收一个下游对象,

fun setObserver(downStream: MlxObserver){

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{

fun map():MlxObservable{}

}

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

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

于是,代码变成了这样:

class MlxObservable{

fun map():MlxObservable{}

}

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

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

不明白?

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

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

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

}

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

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

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

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

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

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

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

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

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

还不明白?

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

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

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

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

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

override fun setObserver(downStream: MlxObserver){

//此时的downStream就是真正的下游

}

}

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

说的真对

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

class MlxMapObservable <T, R>(

private val source:MlxObservableOnSubscribe,

private val func:((T)->R)

):MlxObservableOnSubscribe{

override fun setObserver(downStream: MlxObserver){

//此时的downStream就是真正的下游

}

}

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

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

class MlxMapObservable <T, R>(

private val source:MlxObservableOnSubscribe,

private val func:((T)->R)

):MlxObservableOnSubscribe{

override fun setObserver(downStream: MlxObserver){

//此时的downStream就是真正的下游

}

class MlxMapObserver<T,R>(

private val downStream:MlxObserver,

private val func:((T)->R)

):MlxObserver{

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){

val map=MlxMapObserver(downStream,func)//创建自己的观察者对象

source.subscribe(map)//将自己传递给上游

}

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

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

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

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

//source就是上游真正的被观察者。

val map=MlxMapObservable(this.source!!,func)

return MlxObservable(map)

}

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

MlxObservable.create(object :MlxObservableOnSubscribe{

override fun setObserver(emitter: MlxObserver) {

println(“上游发送数据:10”)

emitter.onNext(10)

}

})

.map{ item->

“这是map操作符转换后的数据:$item”

}

.setObserver(object :MlxObserver{

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 (

val source:MlxObservableOnSubscribe):MlxObservableOnSubscribe{

override fun setObserver(downStream: MlxObserver){

val observer=MlxSubscribeObserver(downStream)

}

class MlxSubscribeObserver(val downStream:MlxObserver):MlxObserver{

}

}

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

fun subscribeOn():MlxObservable{

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来表示策略把。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

image

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-ko7axjoP-1712701071845)]
[外链图片转存中…(img-dhBQktLM-1712701071845)]
[外链图片转存中…(img-dK9RjfcM-1712701071845)]
[外链图片转存中…(img-Z2d6u0P7-1712701071846)]
[外链图片转存中…(img-KHkcMmkF-1712701071846)]
[外链图片转存中…(img-gtZh1B8U-1712701071846)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-9dTMabST-1712701071846)]

最后

针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

[外链图片转存中…(img-Kwl4ssyq-1712701071847)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-FaBwuEe8-1712701071847)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值