RxJava2.x 源码解析(二)线程切换

一、分析observeOn()方法的线程切换

1、示例代码

public static void changeThread() {
    Log.d("TAG", "当前程序所在的线程 " + Thread.currentThread().getName());
    Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
        @Override
        public void subscribe(ObservableEmitter e) throws Exception {
            e.onNext(1);
            e.onNext(2);
            e.onNext(3);
            e.onComplete();
            Log.d("TAG", "被观察者所在的线程 " + Thread.currentThread().getName());
        }
    });
    observable
            .observeOn(Schedulers.io())
            .subscribe(new Observer<Integer>() {
                @Override
                public void onSubscribe(Disposable d) {
                    Log.d("TAG", "subscribe>>onSubscribe");
                }

                @Override
                public void onNext(Integer integer) {
                    Log.d("TAG", "subscribe>>onNext:" + integer);
                    Log.d("TAG", "观察者所在的线程 " + Thread.currentThread().getName());
                }

                @Override
                public void onError(Throwable e) {
                    Log.d("TAG", "subscribe>>onError:" + e.getMessage());
                }

                @Override
                public void onComplete() {
                    Log.d("TAG", "subscribe>>onComplete");
                }
            });
}

日志如下:

D/TAG: 当前程序所在的线程 main
D/TAG: subscribe>>onSubscribe
D/TAG: 被观察者所在的线程 main
D/TAG: subscribe>>onNext:1
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onNext:2
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onNext:3
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onComplete

2、源码分析

observeOn()方法的源码如下:

public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
    ObjectHelper.requireNonNull(scheduler, "scheduler is null");
    ObjectHelper.verifyPositive(bufferSize, "bufferSize");
    return RxJavaPlugins.onAssembly(new ObservableObserveOn<T>(this, scheduler, delayError, bufferSize));
}

最终返回一个ObservableObserveOn对象,ObservableObserveOn是Observable的子类。当调用subscribe()方法时会回调subscribeActual方法,源码如下:

@Override
protected void subscribeActual(Observer<? super T> observer) {
    if (scheduler instanceof TrampolineScheduler) {
        source.subscribe(observer);
    } else {
        Scheduler.Worker w = scheduler.createWorker();
        source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));
    }
}

如果传入的scheduler是Scheduler.trampoline(),Scheduler.trampoline()是指传入当前线程,也就是不做任何线程切换操作。一般情况不会这样操作。所以上面代码会执行else部分。

source(上游Observable)和Observable.subscribe()操作都没有任何变化,唯一改变的地方就是将Observer进行了封装,所以我们可以因此得出结论, Observable.observeOn(Scheduler) 并不会对上游线程执行环境有任何影响。

Observer的onNext()源码如下:

@Override
public void onNext(T t) {
    ......

    if (sourceMode != QueueDisposable.ASYNC) {
        queue.offer(t);
    }
    schedule();
}

void schedule() {
    if (getAndIncrement() == 0) {
        worker.schedule(this);
    }
}

点进schedule发现是Schedule.Worker一个抽象方法,我们要到实现类中查看具体实现,这里使用Schedule.Worker的实现类是IoScheduler.EventLoopWorker。IoScheduler.EventLoopWorker中schedule方法源码如下:

@Override
public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
    if (tasks.isDisposed()) {
        return EmptyDisposable.INSTANCE;
    }
    return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}

scheduleActual()的源码如下:

public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
    ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
    ......
    Future<?> f;
    try {
        if (delayTime <= 0) {
            f = executor.submit((Callable<Object>)sr);
        } else {
            f = executor.schedule((Callable<Object>)sr, delayTime, unit);
        }
        sr.setFuture(f);
    } catch (RejectedExecutionException ex) {
        ......
        RxJavaPlugins.onError(ex);
    }

    return sr;
}

executor是一个ScheduledExecutorService对象,而ScheduledExecutorService的父接口是我们所熟悉的ExecutorService接口,所以很清晰ScheduledExecutorService具有创建和调度线程的能力,而其具体的实现在此就不讨论了。

总结:

onNext(T)方法会触发Scheduler对象的schedule(Runnable, long, TimeUnit),该方法是一个抽象方法,由子类实现,所以才有了多种多样的 Schedulers.io()、Schedulers.computation()、Schedulers.trampoline()等调度器,具体调度器的内部会使用相关的线程来submit()或者schedule()任务。

解决完调度器的问题,那么接下来就是看看Runnable.run()里面的逻辑是什么样的,回到ObserveOnObserver中,源码如下:

@Override
public void run() {
    if (outputFused) {
        drainFused();
    } else {
        drainNormal();
    }
}

outputFused默认是false,所有一般都执行drainNormal(),源码如下:

void drainNormal() {
    final SimpleQueue<T> q = queue;
    final Observer<? super T> a = actual;

    for (;;) {
        if (checkTerminated(done, q.isEmpty(), a)) {
            return;
        }

        for (;;) {
            boolean d = done;
            T v;

            try {
                v = q.poll();
            } catch (Throwable ex) {
                Exceptions.throwIfFatal(ex);
                s.dispose();
                q.clear();
                a.onError(ex);
                worker.dispose();
                return;
            }
            boolean empty = v == null;

            if (checkTerminated(d, empty, a)) {
                return;
            }

            if (empty) {
                break;
            }

            a.onNext(v);
        }
        ......
    }
}

可以看到实际上最后一行执行Observer#onNext(T)方法,也就是意味着ObserveOnObserver 中触发下一层Observer的onNext(T)操作在指定线程执行,也就达到了切换线程的目的。

3、来个复杂的例子

经过 RxJava2.x 源码解析(一)基本订阅流程一文我们知道,Observer 的传递是由下往上的,从源头开始,我们自定义的 Observer向上传递的时候到达第六个Observable 的时候被线程封装了一层,我们不妨使用伪代码演示一下。

new Thread("Scheduler io") {
    @Override
    public void run() {
        // flatMap() 操作
        flatMap();
        System.out.println("flatMap 操作符执行线程:" + Thread.currentThread().getName());
        System.out.println("第二个 observeOn() 执行线程:" + Thread.currentThread().getName());
        // 第二个 observeOn() 操作
        new Thread("Scheduler computation") {
            @Override
            public void run() {
                // map() 操作
                map();
                System.out.println("map 操作符执行线程:" + Thread.currentThread().getName());
                System.out.println("第三个 observeOn() 执行线程:" + Thread.currentThread().getName());
                // 第三个 observeOn() 操作
                new Thread("Android mainThread") {
                    @Override
                    public void run() {
                        // Observer#onNext(T)/onComplete()/onError() 执行线程
                        System.out.println("Observer#onNext(T)/onComplete()/onError() 执行线程:" +
                                           Thread.currentThread().getName());
                    }
                } .start();
            }
        } .start();
    }
} .start();

简而言之Observable.observeOn(Scheduler)的实现原理在于将目标Observer的 onNext(T)、onError(Throwable)、onComplete()置于指定线程中运行。

二、分析subscribeOn()方法的线程切换

1、示例代码

public static void changeThread() {
    Log.d("TAG", "当前程序所在的线程 " + Thread.currentThread().getName());
    Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
        @Override
        public void subscribe(ObservableEmitter e) throws Exception {
            e.onNext(1);
            e.onNext(2);
            e.onNext(3);
            e.onComplete();
            Log.d("TAG", "被观察者所在的线程 " + Thread.currentThread().getName());
        }
    });
    observable
            .subscribeOn(Schedulers.io())
            .subscribe(new Observer<Integer>() {
                @Override
                public void onSubscribe(Disposable d) {
                    Log.d("TAG", "subscribe>>onSubscribe");
                }

                @Override
                public void onNext(Integer integer) {
                    Log.d("TAG", "subscribe>>onNext:" + integer);
                    Log.d("TAG", "观察者所在的线程 " + Thread.currentThread().getName());
                }

                @Override
                public void onError(Throwable e) {
                    Log.d("TAG", "subscribe>>onError:" + e.getMessage());
                }

                @Override
                public void onComplete() {
                    Log.d("TAG", "subscribe>>onComplete");
                }
            });
}

日志如下:

D/TAG: 当前程序所在的线程 main
D/TAG: subscribe>>onSubscribe
D/TAG: subscribe>>onNext:1
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onNext:2
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onNext:3
D/TAG: 观察者所在的线程 RxCachedThreadScheduler-1
D/TAG: subscribe>>onComplete
D/TAG: 被观察者所在的线程 RxCachedThreadScheduler-1

2、源码分析

subscribeOn的源码如下:

public final Observable<T> subscribeOn(Scheduler scheduler) {
    ObjectHelper.requireNonNull(scheduler, "scheduler is null");
    return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));
}

在订阅时最终会调用到ObservableSubscribeOn的subscribeActual方法,我们看看其源码:

@Override
public void subscribeActual(final Observer<? super T> s) {
    final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);

    s.onSubscribe(parent);

    parent.setDisposable(scheduler.scheduleDirect(new Runnable() {
        @Override
        public void run() {
            source.subscribe(parent);
        }
    }));
}

第一行老套路,对下游Observer进行了一层封装,第二行因为它不涉及线程切换所以此处也不做扩展,第三行Scheduler.scheduleDirect(Runnable)就是切换线程的了。scheduleDirect源码如下:

public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
    final Worker w = createWorker();

    final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);

    w.schedule(new Runnable() {
        @Override
        public void run() {
            try {
                decoratedRun.run();
            } finally {
                w.dispose();
            }
        }
    }, delay, unit);

    return w;
}

这里实现线程切换是w中,我们需要看看createWorker的源码:

public abstract Worker createWorker();

在Scheduler中是一个抽象方法,因为我们要去其实现类IoScheduler中看看具体实现:

@Override
public Worker createWorker() {
    return new EventLoopWorker(pool.get());
}

static final class EventLoopWorker extends Scheduler.Worker {
    private final CompositeDisposable tasks;
    private final CachedWorkerPool pool;
    private final ThreadWorker threadWorker;

    final AtomicBoolean once = new AtomicBoolean();

    EventLoopWorker(CachedWorkerPool pool) {
        this.pool = pool;
        this.tasks = new CompositeDisposable();
        this.threadWorker = pool.get();
    }
    
    ......

    @NonNull
    @Override
    public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
        if (tasks.isDisposed()) {
            return EmptyDisposable.INSTANCE;
        }

        return threadWorker.scheduleActual(action, delayTime, unit, tasks);
    }
}

最终是通过threadWorker.scheduleActual(action, delayTime, unit, tasks);实现线程切换,scheduleActual源码如下:

public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
    ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);
    ......
    Future<?> f;
    try {
        if (delayTime <= 0) {
            f = executor.submit((Callable<Object>)sr);
        } else {
            f = executor.schedule((Callable<Object>)sr, delayTime, unit);
        }
        sr.setFuture(f);
    } catch (RejectedExecutionException ex) {
        ......
        RxJavaPlugins.onError(ex);
    }

    return sr;
}

executor是一个ScheduledExecutorService对象,而ScheduledExecutorService的父接口是我们所熟悉的ExecutorService接口,所以很清晰ScheduledExecutorService具有创建和调度线程的能力,而其具体的实现在此就不讨论了。

3、来个复杂的例子

日志如下:

onNext(T)/onError(Throwable)/onComplete() 的执行线程是: io

前面我们分析到Observable.subscribeOn(Scheduler) 实际上是将 Observable.subscribe(Observer)的操作放在指定线程,而通过RxJava2.x源码解析(一)订阅流程一文我们知道了subscribe的过程是由下往上的。所以首先是第三个 Observable调用Observable.subscribe(Observer)启动订阅,在其内部会激活第二个 Observable的Observable.subscribe(Observer)方法,但是此时该方法外部被套入了一个 Schedulers.computation()线程,于是这个订阅的过程就被运行在了该线程中。我们不妨用伪代码演示如下:

new Thread("computation") {
    @Override
    public void run() {
        // 第二个 Observable.subscribe(Observer) 的实质
        // 就是切换线程,效果类似如下
        new Thread("io") {
            @Override
            public void run() {
                // 第一个 Observable.subscribe(Observer) 的实质
                // 就是发射事件
                System.out.println("onNext(T)/onError(Throwable)/onComplete() 的执行线程是: " + Thread
                                   .currentThread().getName());
            }
        } .start();
    }
} .start();

因此使用多个subscribeOn时只有第一个有效。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值