不明白?
就是说,你需要自己去定义转换的规则,根据你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来表示策略把。
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 submitSubscribeWork(
source: MlxObservableOnSubscribe, //上游
downStream: MlxObserver,//下游
thread:Int//指定的线程
) {
when(thread){
IO->{
IOThreadPool.submit {
//从线程池抽取一个线程执行上游和下游的连接操作
source.setObserver(downStream)
}
}
MAIN->{
}
}
}
代码看起来很简单。就是得到上下游之后,在线程池里进行了上下游的连接操作,也就是把下游设置给了上游。我们留下一个main,因为main还没实现,我们一会在实现。现在这个线程池也有了,方法也有了,我们回到切换线程的操作符里把这个切换线程的任务提交给线程池管理类。
class MlxSubscribeObservable (
private val source:MlxObservableOnSubscribe,
private val thread:Int //看这里,新增了指定线程
):MlxObservableOnSubscribe{
override fun setObserver(downStream: MlxObserver){
val observer=MlxSubscribeObserver(downStream)
//提交任务给指定线程,也就是再指定线程完成上下游的链接
Schedulers.INSTANCE.submitSubscribeWork(source,downStream,thread)
}
class MlxSubscribeObserver(val downStream:MlxObserver):MlxObserver{
…
}
}
那么问题又来了,不能光别的线程啊,万一我就想主线程发送怎么办?
所以我们来搞主线程的东西。
说起主线程,我们不得不提到一个东西,没错,就是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 submitSubscribeWork(
source: MlxObservableOnSubscribe,
downStream: MlxObserver,
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 {
Log.i(“zzz”, “上游线程:${Thread.currentThread().name}”)
it.onNext(10)
})
.subscribeOn(Schedulers.IO())
.subscribe(object : MlxObserver {
override fun onNext(item: Int) {
Log.i(“zzz”, “下游线程:${Thread.currentThread().name}”)
}
…
})
OK 完美。成功切换上游线程
不过下游线程怎么也跟着切换了?嗨,RxJava也是这样操作的,因为你没有指定下游的线程,所以就默认上游和下游是一个线程的呀~
那我们依葫芦画瓢,按照上面的切换上游线程也写个切换下游线程的真实的被观察者和观察者,以及方法。
改变下游线程
首先是下游的被观察者对象:
class MlxObserverObservable(
private val source: MlxObservableOnSubscribe,
private val thread: Int
) :MlxObservableOnSubscribe {
override fun setObserver(downStream: MlxObserver) {
val observer = MlxObserverObserver(downStream, thread)
source.subscribe(observer)
}
class MlxObserverObserver(
val downStream: MlxObserver,
val thread: Int
) : MlxObserver {
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)
}
}
}
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
最后送福利了,现在关注我并且加入群聊可以获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,欢迎加群探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿
点击GitHub领取
IOThreadPool?.submit {
function.invoke() //调用高阶函数
}
}
MAIN->{
handler?.let {
val m=Message.obtain(it){
function.invoke()//调用高阶函数
}
it.sendMessage(m)
}
}
}
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-hZioVLez-1710684039969)]
[外链图片转存中…(img-JLbWwbE8-1710684039970)]
[外链图片转存中…(img-RrI1Rq7t-1710684039970)]
[外链图片转存中…(img-B1vRPucs-1710684039971)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-vmUuLSza-1710684039971)]
最后送福利了,现在关注我并且加入群聊可以获取包含源码解析,自定义View,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,欢迎加群探讨,有flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿
点击GitHub领取
[外链图片转存中…(img-9n51EncQ-1710684039972)]