“码上理解”---RxJava 线程调度分析(一)切换子线程

RxJava简单介绍

RxJava现在几乎每个Android项目中都有它的身影,RxJava是使用了Rx(ReactiveX)函数库的语言库,Rx是一个函数库,让开发者可以利用可观察序列和LINQ风格查询操作符来编写异步和基于事件的程序,除了RxJava,Rx几乎支持了全部的流行编程语言,例如:RxJS、Rx.NET等等。

RxJava的优点和使用方法这篇文章不做介绍,今天关注一下框架内部原理:比如我们平时用RxJava的时候会使用subscribeOn(Schedulers.io())和observeOn(AndroidSchedulers.mainThread())来切换线程,有没有对其内部的实现感兴趣呢?
读这篇文章之前可以先看看这篇,有助于理解流程关系 《码上理解— 手撕RxJava订阅关系,事件发送和接收》

文章目的

通过看源码的实现过程,来理解RxJava在Android平台上如何通过subscribeOn(Schedulers.io())方法实现线程调度的

环境搭建和代码示例

示例是基于RxJava2
配置和代码很简单,如下

    implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
public class MainActivity extends AppCompatActivity {
    private Disposable disposable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rxJava();
    }

    public void rxJava() {
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> e) throws Exception {
                e.onNext("事件1");
            }
        })
                .subscribeOn(Schedulers.io())
                //.observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {

                    @Override
                    public void onSubscribe(Disposable d) {
                        Log.i("RxJava",s+","+Thread.currentThread().getName());
                        disposable = d;
                    }

                    @Override
                    public void onNext(String s) {
                        Log.i("RxJava",s);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (disposable != null)
            if (!disposable.isDisposed())
                disposable.dispose();
    }
}

运行后日志打印

I/RxJava: 订阅
I/RxJava: 事件1,RxCachedThreadScheduler-1

文章有点长,可以先拉到底部,先看总结,然后再一步一步跟代码

分析subscribeOn(Schedulers.io())

我们先尝试看一下subscribeOn(Schedulers.io())这个方法
点击subscribe方法进入源码

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

发现进入了抽象类Observable的subscribeOn方法,方法的参数是Scheduler 类型,Scheduler的翻译是调度器,进一步跟踪Scheduler类,发现Scheduler类也是抽象类,以下是部分代码,可以先放上混个眼熟

public abstract class Scheduler {
    ... ...
    @NonNull
    public abstract Worker createWorker();
    ... ...
    @NonNull
    public Disposable scheduleDirect(@NonNull Runnable run) {
        return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
    }
 	... ...
 	public abstract static class Worker implements Disposable {
		@NonNull
        public Disposable schedule(@NonNull Runnable run) {
            return schedule(run, 0L, TimeUnit.NANOSECONDS);
        }
		@NonNull
        public abstract Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit);
	}
	... ...
}

由于Schedulers.io()就是方法实参,所以我们推断Schedulers.io()就是Scheduler的实现类,换句话说Schedulers.io()就是调度器,那我们先看以下这个调度器是什么吧!

Schedulers.io()

我们尝试点击Schedulers类,跳转源码,发现用到了策略模式,Schedulers.io()是其中一个策略
常见的策略的使用场景了解一下

1.Schedulers.io(): I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。
2.Schedulers.computation(): 计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
3.另外, Android 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。

我们看一下Schedulers.io()具体的实现方式

	@NonNull
    public static Scheduler io() {
        return RxJavaPlugins.onIoScheduler(IO);
    }

发现返回了RxJavaPlugins.onIoScheduler,这个类RxJavaPlugins下的方法其实是实现hook,由于我们当前不关心hook,先不用管它,它方法内返回的其实就是IO对象

 /**
     * Calls the associated hook function.
     * @param defaultScheduler the hook's input value
     * @return the value returned by the hook
     */
    @NonNull
    public static Scheduler onIoScheduler(@NonNull Scheduler defaultScheduler) {
        Function<? super Scheduler, ? extends Scheduler> f = onIoHandler;
        if (f == null) {
            return defaultScheduler;
        }
        return apply(f, defaultScheduler);
    }

ok,现在我们看一下IO是哪来的,发现其实在类中的静态代码块中初始化的

 static {
        SINGLE = RxJavaPlugins.initSingleScheduler(new SingleTask());

        COMPUTATION = RxJavaPlugins.initComputationScheduler(new ComputationTask());
		//这里初始化了调度器IO
        IO = RxJavaPlugins.initIoScheduler(new IOTask());

        TRAMPOLINE = TrampolineScheduler.instance();

        NEW_THREAD = RxJavaPlugins.initNewThreadScheduler(new NewThreadTask());
    }

又看到了RxJavaPlugins类,我们先不去管它,直接看new IOTask()

    static final class IOTask implements Callable<Scheduler> {
        @Override
        public Scheduler call() throws Exception {
            return IoHolder.DEFAULT;
        }
    }

发现他是一个任务,返回值是Scheduler 调度器,那它是从哪里执行这个任务的呢,我们这时可以看RxJavaPlugins.initNewThreadScheduler这个方法了

 @NonNull
    public static Scheduler initIoScheduler(@NonNull Callable<Scheduler> defaultScheduler) {
        ObjectHelper.requireNonNull(defaultScheduler, "Scheduler Callable can't be null");
        //代码中如果没有实现onInitIoHandler,这里始终是null的
        Function<? super Callable<Scheduler>, ? extends Scheduler> f = onInitIoHandler;
        if (f == null) {
            return callRequireNonNull(defaultScheduler);
        }
        return applyRequireNonNull(f, defaultScheduler);
    }

我们传入了Callable,返回的是Scheduler ,说明在这个方法中执行了IOTask 任务,于是我们进一步看方法callRequireNonNull(defaultScheduler)。
这里还要说明一下,如果我们没有自己实现hook方法setInitIoSchedulerHandler(), onInitIoHandler这里始终是null,所以我们直接看一个参数的方法callRequireNonNull

@NonNull
    static Scheduler callRequireNonNull(@NonNull Callable<Scheduler> s) {
        try {
            return ObjectHelper.requireNonNull(s.call(), "Scheduler Callable result can't be null");
        } catch (Throwable ex) {
            throw ExceptionHelper.wrapOrThrow(ex);
        }
    }

ok,我们发现s.call(),执行了IOTask任务。我们再回到IOTask,我们看看到底返回的调度器是个什么东东

    static final class IoHolder {
        static final Scheduler DEFAULT = new IoScheduler();
    }

发现IoHolder 其实是Schedulers的静态内部类,静态内部类中又创建了静态对象IoScheduler,看一下
IoScheduler

/**
 * Scheduler that creates and caches a set of thread pools and reuses them if possible.
 */
public final class IoScheduler extends Scheduler {
static {
       	...
       	//工作线程的线程工厂
       	WORKER_THREAD_FACTORY = new RxThreadFactory(WORKER_THREAD_NAME_PREFIX, priority);
       	//线程池
        NONE = new CachedWorkerPool(0, null, WORKER_THREAD_FACTORY);
        NONE.shutdown();
    }
	//无参构造
    public IoScheduler() {
        this(WORKER_THREAD_FACTORY);
    }
    //传入了用于创建工作线程的线程工厂
    public IoScheduler(ThreadFactory threadFactory) {
        this.threadFactory = threadFactory;
        this.pool = new AtomicReference<CachedWorkerPool>(NONE);
        start();
    }

    public IoScheduler() {
        this(WORKER_THREAD_FACTORY);
    }
    
    // 实现了 Scheduler 的createWorker方法
    @NonNull
    @Override
    public Worker createWorker() {
        return new EventLoopWorker(pool.get());
    }
	...
	//实现了Scheduler.Worker 抽象类
    static final class EventLoopWorker extends Scheduler.Worker {
		...
		@NonNull
        @Override
        public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {
            if (tasks.isDisposed()) {
                // don't schedule, we are unsubscribed
                return EmptyDisposable.INSTANCE;
            }

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

上面的代码我们可以看出,调度器里其实是创建了线程池,所以,此处可以推测.subscribeOn(Schedulers.io())方法将每一个事件当作任务放入线程池执行,从而达到线程血环的目的。
再注意一点,IO调度器IoScheduler类中实现了一个抽象方法,一个抽象类,还记得文章一开始混眼熟的那个类吗,ok,我们再眼熟一下因为一会要用
在这里插入图片描述
到目前为止,我们知道Schedulers.io()其实就是创建了IoScheduler,它的本质是一个线程池,由方法subscribeOn(Schedulers.io()),IoScheduler对象丢给了subscribeOn方法,那我们进一步看一下subscribeOn方法中究竟是卖的什么药

subscribeOn()

subscribeOn我们接着看

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

前面如果都跟者看过来的话,我们就可以直接进入ObservableSubscribeOn类了(RxJavaPlugins.onAssembly()方法返回的就是ObservableSubscribeOn对象)
ObservableSubscribeOn类我全粘过来了,代码比较长

public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
    final Scheduler scheduler;//IoScheduler

    public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
        super(source);
        this.scheduler = scheduler;
    }

    @Override
    public void subscribeActual(final Observer<? super T> s) {
    	//包装一层,将观察者Observer放入SubscribeOnObserver对象中
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
		//调用观察者Observer的onSubscribe方法
        s.onSubscribe(parent);

        parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
    }

    static final class SubscribeOnObserver<T> extends AtomicReference<Disposable> implements Observer<T>, Disposable {

        private static final long serialVersionUID = 8094547886072529208L;
        final Observer<? super T> actual;

        final AtomicReference<Disposable> s;

        SubscribeOnObserver(Observer<? super T> actual) {
            this.actual = actual;
            this.s = new AtomicReference<Disposable>();
        }

        @Override
        public void onSubscribe(Disposable s) {
            DisposableHelper.setOnce(this.s, s);
        }

        @Override
        public void onNext(T t) {
            actual.onNext(t);
        }

        @Override
        public void onError(Throwable t) {
            actual.onError(t);
        }

        @Override
        public void onComplete() {
            actual.onComplete();
        }

        @Override
        public void dispose() {
            DisposableHelper.dispose(s);
            DisposableHelper.dispose(this);
        }

        @Override
        public boolean isDisposed() {
            return DisposableHelper.isDisposed(get());
        }

        void setDisposable(Disposable d) {
            DisposableHelper.setOnce(this, d);
        }
    }

    final class SubscribeTask implements Runnable {
        private final SubscribeOnObserver<T> parent;

        SubscribeTask(SubscribeOnObserver<T> parent) {
            this.parent = parent;
        }

        @Override
        public void run() {
            source.subscribe(parent);
        }
    }
}

拿到这个类,我们可以知道ObservableSubscribeOn对象中有传入的IoScheduler调度器,有一个subscribeActual方法,有一个SubscribeOnObserver静态内部类,还有一个订阅任务SubscribeTask。
如果看过我另一篇文章的码上理解— 手撕RxJava订阅关系,事件发送和接收
应该知道,subscribeActual这个方法其实是由下游subscribe方法调用,也就是执行subscribe就执行subscribeActual
那重点就是subscribeActual方法,看一下subscribeActual方法干了些什么

    @Override
    public void subscribeActual(final Observer<? super T> s) {
    	//包装一层,将观察者Observer放入SubscribeOnObserver对象中
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);
		//调用观察者Observer的onSubscribe方法
        s.onSubscribe(parent);
		//最后创建了一个任务SubscribeTask,把任务丢给IoScheduler调度器
        parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
    }

先把下游的Observer对象包装进SubscribeOnObserver对象,然后执行了observer的onSubscribe方法,表示订阅完成,最后创建了一个任务SubscribeTask,把任务丢给IoScheduler调度器
那IoScheduler的scheduleDirect方法具体干了些什么呢,还记得文章开头混脸熟的Scheduler类吗?
在这里插入图片描述
scheduler.scheduleDirect实际上就是调用到这里了,我们再看一下三个参数的scheduleDirect方法

public abstract class Scheduler {
	@NonNull
	    public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
	        //还记得IoScheduler 的createWorker方法吗
	        final Worker w = createWorker();
			// SubscribeTask
	        final Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
			// 给SubscribeTask包装一层DisposeTask,实现了Disposable接口
	        DisposeTask task = new DisposeTask(decoratedRun, w);
			// 执行 EventLoopWorker的schedule方法
	        w.schedule(task, delay, unit);
	
	        return task;
	    }
}

上面注释已经解释了,scheduleDirect方法就是将SubscribeTask丢给了schedule方法,那看一下schedule方法具体干了啥
在这里插入图片描述
在这里插入图片描述
方法中线程池executor执行了SubscribeTask任务,我们再看一下SubscribeTask的run方法中执行的是什么

public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {
    final Scheduler scheduler;

    public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
        super(source);
        this.scheduler = scheduler;
    }
    ...
    ...
    final class SubscribeTask implements Runnable {
        private final SubscribeOnObserver<T> parent;

        SubscribeTask(SubscribeOnObserver<T> parent) {
            this.parent = parent;
        }

        @Override
        public void run() {
        	//执行了上游的subscribe方法
            source.subscribe(parent);
        }
    }
}

run方法中调用了source的subscribe方法。source是SubscribeOnObserver构造方法传入进来的,它其实就是Observable
在这里插入图片描述
那source.subscribe就是调用的Observable的subscribe方法,ok,找一下subscribe
在这里插入图片描述
如果看过我另一篇文章的码上理解— 手撕RxJava订阅关系,事件发送和接收的老铁应该知道,subscribeActual是抽象方法,subscribeActual(observer)是跳转到哪里,没错是Observable.create创建出的ObservableCreate对象里的subscribeActual方法。
还记得subscribeActual(observer)的参数observer是什么吗,是被SubscribeOnObserver包装的观察者Observer,来看一下ObservableCreate对象中的subscribeActual方法做了什么
在这里插入图片描述
在这里插入图片描述
上图中为什么说e.onNext方法执行在子线程中呢?

因为下游执行subscribe方法的时候,
1.先将Observer观察者包装到SubscribeOnObserver中去(装饰模式)
2.然后执行了Observer的onSubscribe回调,订阅完成
3.创建一个任务SubscribeTask,将装有观察者Observer的包裹SubscribeOnObserver传给这个任务,方便在run方法中将包裹SubscribeOnObserver传给上游,上游拿到包裹可以再进行添加包裹或者解开包裹执行Observer的onNext回调或其它回调
4.将任务SubscribeTask丢进IO调度器IoScheduler中的线程池并submit执行
5.SubscribeTask中的 source.subscribe(parent);将包裹SubscribeOnObserver传给上游,由于此时已经运行在线程池中了,所以上游的subscribeActual方法是运行在子线程中,ObservableOnSubscribe的subscribe回调是在subscribeActual方法中调用的,所以subscribe方法也是运行在子线程,e.onNext在方法subscribe里调用,所以e.onNext方法执行也在子线程中。没毛病。

总结

RxJava通过subscribeOn(Schedulers.io())切换子线程的原理是将subscribeOn的上游都放入线程池中执行;从这里也可以推断出另一个结论,当有多个subscribeOn的时候,只有上游的第一个subscribeOn有效,换句话说程序会运行在上游第一个subscribeOn()所切换的线程里。
最后来张图总结一下
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章