RxJava源码分析(subscribeOn,observeOn与线程切换)

RxJava简介

RxJava是现在大火的一个异步框架,他代码简洁好懂,功能强大,扩展性高,维护难度小。RxJava最吸引人的功能就是它的线程切换功能。在Android开发中,UI组件只能在主线程中进行,但是主线程中执行太复杂的逻辑又会导致APP卡顿,因此灵活的线程切换是一个安卓开发工程师的必会技能。RxJava提供了优秀的线程切换能力,能在不同的线程执行规定的逻辑代码。

这次就来聊一聊RxJava是怎么实现线程切换。

首先RxJava的一切转换功能都是基于lift这个方法产生代理对象完成,关于这部分我之前有写过一篇博客。
RxJava的源码分析(map,flatmap和类型变换)

大家可以先了解一下lift方法的工作原理,本文就不在赘述了。

示例代码

public class RxJavaTest_1 {
    public static void main(String[] args) throws InterruptedException {
        Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                //打印call方法所在线程
                System.out.println("call : " + Thread.currentThread());
                subscriber.onNext("doOnNext");
                subscriber.onCompleted();
            }
        })
        .subscribeOn(Schedulers.newThread())

        .observeOn(Schedulers.newThread())

        .subscribe(new Subscriber<String>() {
            @Override
            public void onCompleted() {
                System.out.println("onCompleted : " + Thread.currentThread());
            }
            @Override
            public void onError(Throwable throwable) {
                System.out.println("onError : " + Thread.currentThread());
            }
            @Override
            public void onNext(String s) {
                //答应onNext方法所在线程
                System.out.println("onNext : " + Thread.currentThread());
            }
        });

        Thread.sleep(1000);
        //打印主线程
        System.out.println("main : " + Thread.currentThread());
    }
}

RxJava提供subscribeOn和observeOn两个方法去更换线程,subscribeOn可以指定subscribe方法运行的线程,observeOn可以指定Subscriber对象的方法被回调时执行的线程。

下图是上面代码的执行结果

我指定subscribe方法运行和Subscriber对象的方法被回调时执行都是在新线程中进行。因此可以看到call方法是运行在一个新线程中的,而onNext和onCompleted两个则是运行在另外的一个线程中的,并且这两个线程都不是主线程。

线程转换源码分析

subscribeOn()和observeOn()两个转换线程的方式都差不多,因此我就用subscribeOn()进行分析。

首先进入subscribeOn()方法。

(注:本文中截取的代码并不是完整的源码,只截取核心代码)

public final Observable<T> subscribeOn(Scheduler scheduler) {
        return create(new OperatorSubscribeOn(this, scheduler));
    }

Scheduler对象是用来描述线程的抽象类,这里需要一个Scheduler对象,由于我们使用的newThread()方法得到了Scheduler,因此目前的Scheduler引用指向的是NewThreadScheduler实现类。

接着产生了一个OperatorSubscribeOn对象。看类名就知道这是个转换器类,而且是用来转换线程的。

来看看这个类吧。

public final class OperatorSubscribeOn<T> implements OnSubscribe<T> {
    final Scheduler scheduler;
    final Observable<T> source;

    public OperatorSubscribeOn(Observable<T> source, Scheduler scheduler) {
        this.scheduler = scheduler;
        this.source = source;
    }
    .....
}

这个类中记录了原始的Observable和目标线程的信息,而且还实现OnSubscribe接口,我好像隐隐感觉到了什么。

接着看这个create方法,这个方法大家都很熟悉的啊,就是用来创建Observable对象的。

public static <T> Observable<T> create(Observable.OnSubscribe<T> f) {
        return new Observable(hook.onCreate(f));
}

果然是这样没错的。到目前为止,subscribeOn方法执行结束。RxJava又创建了一个代理Observable对象出来。

接着我们调用了代理Observable的subscribe方法,传入了一个Subscriber对象。在subscribe方法中Observable对象使用内部类OnSubscribe调用模板方法call。由于这里OnSubscribe对象是OperatorSubscribeOn这个类,因此来看看它的call方法里有什么。

public void call(final Subscriber<? super T> subscriber) {
        //创建一个Worker对象
        final Worker inner = this.scheduler.createWorker();
        subscriber.add(inner);
        inner.schedule(new Action0() {
            public void call() {
                final Thread t = Thread.currentThread();
                //产生代理Subscriber
                Subscriber<T> s = new Subscriber<T>(subscriber) {
                    public void onNext(T tx) {
                        subscriber.onNext(tx);
                    }

                    public void onError(Throwable e) {
                        try {
                            subscriber.onError(e);
                        } finally {
                            inner.unsubscribe();
                        }

                    }

                    public void onCompleted() {
                        try {
                            subscriber.onCompleted();
                        } finally {
                            inner.unsubscribe();
                        }

                    }

                    public void setProducer(final Producer p) {
                        subscriber.setProducer(new Producer() {
                            public void request(final long n) {
                                if (t == Thread.currentThread()) {
                                    p.request(n);
                                } else {
                                    inner.schedule(new Action0() {
                                        public void call() {
                                            p.request(n);
                                        }
                                    });
                                }

                            }
                        });
                    }
                };
                //操作代理对象
                OperatorSubscribeOn.this.source.unsafeSubscribe(s);
            }
        });
    }

这个call方法就是线程转换的核心方法。

首先在call方法中创建了一个Worker对象。

我们使用的是NewThreadScheduler示例,因此产生的是一个NewThreadWorker对象。这里要看看NewThreadWorker的构造方法。

    public NewThreadWorker(ThreadFactory threadFactory) {
        ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, threadFactory);
        boolean cancelSupported = tryEnableCancelPolicy(exec);
        if (!cancelSupported && exec instanceof ScheduledThreadPoolExecutor) {
            registerExecutor((ScheduledThreadPoolExecutor)exec);
        }

        this.schedulersHook = RxJavaPlugins.getInstance().getSchedulersHook();
        this.executor = exec;
    }

看到它悄悄创建了一个线程池,还是只有一个核心线程的那种。

然后这个对象又调用了schedule()方法,这个方法需要一个Action0实参,那就给他创建一个,这个Action0对象实现了call()方法,call方法中新建了一个Subscriber类型代理,回调了原始call()方法传入的Subscriber对象的方法,然后在底部的OperatorSubscribeOn.this.source.unsafeSubscribe(s)这行代码使用了代理对象。

现在有了Action0对象,进入schedule方法。

public Subscription schedule(Action0 action) {
        return this.schedule(action, 0L, (TimeUnit)null);
    }

public Subscription schedule(Action0 action, long delayTime, TimeUnit unit) {
        return this.scheduleActual(action, delayTime, unit));
    }

public ScheduledAction scheduleActual(Action0 action, long delayTime, TimeUnit unit) {
        Action0 decoratedAction = this.schedulersHook.onSchedule(action);
        ScheduledAction run = new ScheduledAction(decoratedAction);
        Object f;
        if (delayTime <= 0L) {
            f = this.executor.submit(run);
        } else {
            f = this.executor.schedule(run, delayTime, unit);
        }

        run.add((Future)f);
        return run;
}

一路来到scheduleActual()方法。Action0对象被包装了一下,这里默认的情况下就是返回原本的Action0对象。

下面利用Action0对象生成了一个ScheduledAction对象,继续往下看。

executor.submit(run)。

果然使用了线程池执行了ScheduledAction对象的代码。

哎,等等,submit方法不是只接收Callable实现类和Runable实现类么?

现在来看看ScheduledAction类。

public final class ScheduledAction extends AtomicReference<Thread> implements Runnable, Subscription {
    final Action0 action;

    public ScheduledAction(Action0 action) {
        this.action = action;
    }

    public void run() {
        try {
            this.action.call();
        } catch (Throwable var7) {
            ......
        } finally {
            this.unsubscribe();
        }
    }
}

ScheduledAction类果然实现了Runnable接口,并且存储了Action0的信息。它的run方法中调用了Action0的call()方法。

回顾一下Action0的call方法中有什么,一个代理的Subscriber对象,然后还用 OperatorSubscribeOn.this.source.unsafeSubscribe(s)操作了这个代理对象。这里的source就是之前保存的原始Observable对象。这不就是代理Observable对原始Observable对象的回调嘛。到这里整个流程应该就有点眉目了。

再进入unsafeSubscribe()方法。

public final Subscription unsafeSubscribe(Subscriber<? super T> subscriber) {
        try {
            subscriber.onStart();
            hook.onSubscribeStart(this, this.onSubscribe).call(subscriber);
            return hook.onSubscribeReturn(subscriber);
        } catch (Throwable var6) {
            Throwable e = var6;
            Exceptions.throwIfFatal(var6);

            try {
                subscriber.onError(hook.onSubscribeError(e));
            } catch (Throwable var5) {
                Exceptions.throwIfFatal(var5);
                RuntimeException r = new RuntimeException("Error occurred attempting to subscribe [" + var6.getMessage() + "] and then again while trying to pass to onError.", var5);
                hook.onSubscribeError(r);
                throw r;
            }

            return Subscriptions.unsubscribed();
        }
    }

这个方法就是整个拼图的最后一块。在这个方法中,原始Observable对Subscriber对象进行了全套的操作,onStart()方法,onError()方法这些个流程方法都有,还有原始Observable中内部对象OnSubscribe的call方法,这里包含了执行onNext()方法的逻辑。

示例代码执行(只包含subscribeOn方法)

首先创建了一个Observable对象OA,实现了OnSubscribe对象并重写了call方法。

执行subscribeOn()方法。在这个方法中通过OperatorSubscribeOn对象记录了原始的OA,并创建了代理对象OB。

执行subscriber()方法。传入一个Subscriber对象SA。这时代理对象OB会执行call方法,call方法中有创建了包含线程池的Worker对象,调用Work对象scheduler方法,该方法正是把一个任务加入到线程池,由一个新线程去执行任务。

这个任务则是由Action0实例构成ScheduledAction对象,其run方法中调用了Action0的call方法。

Action0的call方法中是对SA进行一个代理,产生代理对象SB,最后由上层的OA操作SB。SB又回调了嵌套其中的SA的方法,然后运行结束,方法出栈。

小结

可以看出,RxJava的线程变换也是基于对象实现的。新线程得执行时基于一个新的代理Observable完成,并回调原始的Observable的方法。这样有个好处就是对整个处理流程的影响非常小。可以通过map或flatmap产生多个Observable,进而多次转换线程,给RxJava带来无限可能。

这篇文章只分析了Schedulers.newThread()的情况, 其他情形也是类似的,大家可以自己查看源码。

源码分析的文章都比较长,谢谢你能看到这里,如果文章中有错误的地方或者更好的建议,希望你能指出噢。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值