RXJava2响应式编程框架设计<二>---Rxjava2线程切换原理再梳理

在上一次https://www.cnblogs.com/webor2006/p/12329139.html中对于RxJava2进行了一些简单入门,这次则从源码的角度对它的原理进行一个分析,其中重点是分析线程切换的原因,这个在之前的https://www.cnblogs.com/webor2006/p/10545699.html已经也分析得比较透彻了,这里相当于再查漏补缺吧。

咱们以这个样式的代码进行原理剖析:

just():

点击进去看一下它的源码:

 

其中Observable类中有各种各样的操作符,它的代码量极大:

好,回到主流上来:

这句话的核心其实就是ObservableJust,在之前的分析对于RxJavaPlugins.onAssembly()一带而过了,其实它是一个hook函数【关于hook函数的介绍可以参考:https://www.cnblogs.com/webor2006/p/12268799.html】,何以见得?

那这次来看一下这个勾子函数是如何发挥作用的?

其中onObservableAssembly成员变量默认肯定是为null的,所以最终直接将咱们的source给返回的:

也就是:

好,那如果想要勾子函数发挥作用呢?当然得要来设置勾子函数才行喽,所以咱们来看一下勾子函数的赋值是在哪?

好,回到主流程来,经过这个just()操作符之后,目前创建了一个ObservableJust对像,看一下它的结构:

而Observable又实现了一个接口:

 

好的,继续往下。

subscribeOn():

这里有个关系需要注意哈!!就是此时的Observable对象是通过just()调用返回的ObservableJust哈,由于所有的操作符都是定义在了父类Observable上了,所以我们在链接每一个操作符的源码时都会链到Observable类而非真实的Observable的子类,这个一定得要记住,知道这层关系才有助于我们更好的理解其原理,所以:

接着再来看一下这个包装类ObservableSubscribeOn,猜它又是一个包装了Observable的子类: 

好,回到主流程来,接下来还有一个参数还没有分析:

各处都能看到勾子函数,此时再来看一下IOTask:

此时就将这上Scheduler作为第二个参数传到Observable中:

好,这个操作符先分析到这,继续再来分析。

observeOn():

其中也传来一个Scheduler,来瞅一下这个:

此时这个Scheduler是在rxandroid库中:

好,再回到主流程中来看操作符内部的细节:

此时再来看一下ObservableObserveOn类:

其中这个source很显然是调用了subscribeOn()生成的那个包装被观察者ObservableSubscribeOn。

好,目前来看一下经过了observeOn()操作符调用之后的被观察者的类层次结构:

subscribe(new Observer()) :

ObservableObserveOn.subscribeActual():

接下来则开始订阅,将被观察者与观察者进行关联:

发现它是一个接口:

咋感觉这好像是一个接口回调呢?

好,究竟这个观察者的回调方法是如何调用的,接着来分析最核心的订阅方法了:

这里先再提醒一下,此时的Observable是指observeOn()操作符所返回的ObservableObserveOn被观察者包装类,要时刻清楚这个观察者是谁,好继续往下分析:

此时则回到ObservableObserveOn中进行方法的分析:

此时的scheduler成员变量应该是它返回的:

也就是:

所以此时则会走else条件:

关于这块的代码在之前RxJava原理剖析中已经看过了,这里再来梳理一遍:

此时转到HandlerScheduler:

而HandlerWorker定义如下:

    private static final class HandlerWorker extends Worker {
        private final Handler handler;

        private volatile boolean disposed;

        HandlerWorker(Handler handler) {
            this.handler = handler;
        }

        @Override
        public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
            if (run == null) throw new NullPointerException("run == null");
            if (unit == null) throw new NullPointerException("unit == null");

            if (disposed) {
                return Disposables.disposed();
            }

            run = RxJavaPlugins.onSchedule(run);

            ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);

            Message message = Message.obtain(handler, scheduled);
            message.obj = this; // Used as token for batch disposal of this worker's runnables.

            handler.sendMessageDelayed(message, unit.toMillis(delay));

            // Re-check disposed state for removing in case we were racing a call to dispose().
            if (disposed) {
                handler.removeCallbacks(scheduled);
                return Disposables.disposed();
            }

            return scheduled;
        }

        @Override
        public void dispose() {
            disposed = true;
            handler.removeCallbacksAndMessages(this /* token */);
        }

        @Override
        public boolean isDisposed() {
            return disposed;
        }
    }

好,回到主流程来往下:

此时先来看一下参数,将创建的worker对象包装到一个新的观察者对象中:

此时就得看一眼ObserveOnObserver观察者对象了:

除了实现了两个接口之外,还继承了一个BasicIntQueueDisposable,看到Disposable关键字了,很眼熟嘛,因为在观察者的回调中就有它:

先看一下它的类继承体系:

好,回到主流程:

它的子类为抽象的被观察者:

看一下它的subscribe()的实现:

接下来则又要调用子类的subscribeActual()方法,而当前的子类又是谁呢?得看一下这个source是谁?

很显然它是调用上一个操作符所生成的被观察者对象:

调用了这个操作符所返回的被观察者,也就是:

ObservableSubscribeOn.subscribeActual():

所以,此时转到这个类中来查看一下订阅的详情:

1、包装观察者:

此时看一下该包装观察者:

其中它里面的观察者actual构造参数是来自于:

 

2、调用观察者的onSubscribe()方法:

也就是会调用到这:

此时来看一下这个方法的核心代码:

而其中actual则是:

也就是:

呃,貌似跟我以前的理解有出入,我一直以为:

 

所以为了验证咱们所分析的,我们用实验来确认一下:

运行结果:

呃,颠覆了我的认知,确实是在主线程,好,那如果修改一下程序再来看一下:

 

也就是确实如源码有分析的那样,这个onSubscribe是处于当前线程,那当前线程是以哪个为基准呢?是它:

3、设置被观察者的运行线程:

这里的参数套得比较深,从最里层开始分析:

注意将第一步骤封装的parent观察者给传到递了SubscribeTask当中了:

其中它的run方法中是调用的source的subscribe()方法了,那这个source是啥呢?很明显是它上一个操作符所创建的被观察者:

它的上一个被观察者为:

也就是这个操作符所创建的被观察者:

所以,此时的source为ObservableJust:

至于这个run()是何时调用的目前还不得而知,好,回到主流程再来往外层分析:

其中scheduler是从构造方法中传过来的:

而它是从这传过来的:

它为:

也就是:

分析了这么多,这里用图来对其以上流程进行一个总结:

Scheduler.scheduleDirect():

接下来的流程得看好了,切线程的逻辑就在其中,这里看一下scheduleDirect方法的细节:

上面的这个代码就是切线程的核心啦,好好细品,注意这个方法的Runnable对象则为:

好接下来分析一下scheduleDirect()方法:

1、创建一个Worker线程:

2、钩子函数,若无扩展性特殊处理则返回参数本身:

这个代码就没啥可说的,默认直接就是我们参数的Runnable,再来强调一下这个run参数是它:

3、声明一个处理任务,将Runnable和Worker封装成DisposeTask:

其中在它的run()方法中就调用了咱们的那个DisposeTask的run()方法了,如下:

那这个新封装的DisposeTask又何时调用呢?继续往下分析: 

4、调取Worker对象的schedule()方法:

此时就会调用Worker的schedule()方法了,将新创建的DisposeTask传进去,跟进去瞅一下它的细节:

此时又得来看具体类的实现了:

此时需要注意!

它是咱们新创建的Task,如下:

由于层次非常之深,也很容易看晕,关键的东东一定得要记清楚,这样分析才不会晕,好,此时再往下跟:

此时看一下ScheduledRunnable:

其中看一下executor是否是线程池,确认一下:

还是回到主流程继续,我们还得回溯本源,还没完哈:

此时的actual为在这块新创建的Task,如下:

好,此时它的run()就得到执行了:

这里再来啰嗦一下:

也就是:

那此时看一下SubscribeTask的run()方法:

ObservableJust.subscribeActual():

此时则转到ObservableJust来:

接下来则来分析这个方法的流程,先来明白下这个参数s是啥?很明显是SubscribeOnObserver,也就是:

然后跟进去瞅一眼ScalarDisposable:

好回到主流程:

 

此时:

 

此时则会回调:

此时的actual为:

所以流程得会流转到这:

此时的worker还记得是啥不?说实话分析到这也晕晕的了,这里再来回忆一下:

由于我们是指定的UI线程:

所以createWorker()的细节在:

然后再看一下它里面的schedule()方法的逻辑:

注意在生成ScheduledRunnable对象时的第二个run参数,其实是传的它:

很明显已经开始切到UI线程了,最终会执行ScheduledRunnable.run()方法:

 

此时又会调回到这个地方,注意此时已经切到了UI线程了哈:

好熟悉的三个回调呀,那此时看一下actual又是谁?其实是它:

 

也就是我们的匿名观察者类:

好,这里再回到这块分析一下落掉的这句代码:

此时的s是啥来着?

所以,此时转到它的onSubscribe()方法中:

 

也就是说:

其实subscribeOn()是来决定被观察者的subscribe()事件发送的线程的,下面来看一下:

输出:

此时咱们利用subscribeOn()切换一下线程:

再运行:

最后稍加总结一下关于SubscribeOnObserver的流程:

 

至此,关于整个线程切换的完整流程就已经分析完了,太tm复杂了,真佩服写这框架的人是啥神脑,太逆天了,真是晕晕的~~不过这次完整走一遍之后,确实对于整个RxJava的原理的了解又更进一步了,时间花得还是值得的。

posted on 2020-02-23 13:27  cexo  阅读(584)  评论(0编辑  收藏  举报

导航