☝点击上方蓝字,关注我们!
1. 基本介绍
Scheduler是RxJava对线程控制器的一个抽象,RxJava内置多个Scheduler的实现,如下表:
|
Scheduler |
作用 |
|
single |
使用定长为1的线程池(new Scheduled Thread Pool(1)),重复利用这个线程。 |
|
newThread |
每次都启用新线程,并在新线程中执行操作。 |
|
computation |
使用的固定的线程池(Fixed Scheduler Pool),大小为CPU核数,适用于CPU密集型计算。 |
|
io |
适合I/O操作(读写文件,读写数据库,网络信息交互等)所使用的Scheduler。行为模式和newThread()差不多,区别在于io()的内部实现使用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下,io()比new Thread()更有效率。 |
|
trampoline |
直接在当前线程运行,如果当前线程有其他任务正在执行,则会先暂停其他任务。 |
|
Schedulers.from |
将java.util.concurrent.Executor转换成一个调度器实例,即可以自定义一个Executor来作为调度器。 |
线程调度:
默认情况下,Observable和Observer处于同一线程中,如果想要切换线程,则可以使用subscribeOn()和observeOn()。
运行如下代码:
1private void schedulerTest() { 2 Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() { 3 @Override 4 public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception { 5 Log.i(TAG, "create:" + Thread.currentThread().getName()); 6 observableEmitter.onNext(1); 7 observableEmitter.onComplete(); 8 } 9 });10 observable.subscribeOn(Schedulers.newThread())11 .subscribeOn(Schedulers.io())12 .observeOn(Schedulers.io())13 .map(new Function<Integer, Integer>() {14 @Override15 public Integer apply(@NonNull Integer integer) throws Exception {16 Log.i(TAG, "map1:" + Thread.currentThread().getName());17 return integer;18 }19 })20 .observeOn(Schedulers.newThread())21 .map(new Function<Integer, Integer>() {22 @Override23 public Integer apply(@NonNull Integer integer) throws Exception {24 Log.i(TAG, "map2:" + Thread.currentThread().getName());25 return integer;26 }27 })28 .observeOn(AndroidSchedulers.mainThread())29 .doOnSubscribe(new Consumer<Disposable>() {30 @Override31 public void accept(@NonNull Disposable disposable) throws Exception {32 Log.i(TAG, "doOnSubscribe:" + Thread.currentThread().getName());33 }34 })35 .subscribeOn(Schedulers.single())36 .subscribe(new Consumer<Integer>() {37 @Override38 public void accept(@NonNull Integer integer) throws Exception {39 Log.i(TAG, "subscribe:" + Thread.currentThread().getName());40 }41 });42}
我们先直接看一下运行结果:
在我们使用RxJava的时候,其实是大致知道线程调度的规则:subscribeOn()是影响上游线程调度,且只有最上面一次起作用。observeOn()影响下游线程调度,每次都起作用。而这个规则的具体的实现过程是怎么样的呢?
我们从源码看一下subscribeOn()和observeOn()是如何做线程调度的。
2. 源码解析:subscribeOn()
如示例代码:
subscribeOn(Schedulers.single()):会创建一个SingleScheduler对象:
1public SingleScheduler(ThreadFactory threadFactory) { 2 this.threadFactory = threadFactory; 3 executor.lazySet(createExecutor(threadFactory)); 4} 5 6static ScheduledExecutorService createExecutor(ThreadFactory threadFactory) { 7 return SchedulerPoolFactory.create(threadFactory); 8} 9public static ScheduledExecutorService create(ThreadFactory factory) {10 final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);11 tryPutIntoPool(PURGE_ENABLED, exec);12 return exec;13}
这时,一个single的线程池就已经被创建出来,等待分配任务进行调度。我们从第4节可以知道,在逆向订阅的过程中,会执行到ObservableSubscribeOn的如下代码:
1@Override2public void subscribeActual(final Observer<? super T> s) {3 final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);45 s.onSubscribe(parent);67 parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));8}
scheduler会执行scheduleDirect()方法,传了一个SubscribeTask对象,实际上就是对Runnable进行了封装,接下来我们看scheduleDirect都做了什么:
1@NonNull2public Disposable scheduleDirect(@NonNull Runnable run) {3 return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);4}
1@NonNull 2public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) { 3 final Worker w = createWorker(); 4 5 final Runnable decoratedRun = RxJavaPlugins.onSchedule(run); 6 7 DisposeTask task = new DisposeTask(decoratedRun, w); 8 9 w.schedule(task, delay, unit);1011 return task;12}
首先创建一个Worker对象,跳过错误检查,将已传入的Runnable对象,封装成一个DisposeTask的Runnable对象,交由Worker处理。参照示例代码,我们在subscribeOn时传入的是一个single的scheduler,在这个时候,会返回一个如下的Worker对象:
1@NonNull2@Override3public Worker createWorker() {4 return new ScheduledWorker(executor.get());5}
之后,Worker会执行schedule()方法,所以我们继续看ScheduledWorker的schedule()方法的执行:
1@NonNull 2@Override 3public Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) { 4 if (disposed) { 5 return EmptyDisposable.INSTANCE; 6 } 7 8 Runnable decoratedRun = RxJavaPlugins.onSchedule(run); 910 ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, tasks);11 tasks.add(sr);1213 try {14 Future<?> f;15 if (delay <= 0L) {16 f = executor.submit((Callable<Object>)sr);17 } else {18 f = executor.schedule((Callable<Object>)sr, delay, unit);19 }2021 sr.setFuture(f);22 } catch (RejectedExecutionException ex) {23 dispose();24 RxJavaPlugins.onError(ex);25 return EmptyDisposable.INSTANCE;26 }2728 return sr;29}
在这个方法里面,会传入一个Runnable对象,封装成一个SchedulerRunnable对象:
1public ScheduledRunnable(Runnable actual, DisposableContainer parent) {2 super(3);3 this.actual = actual;4 this.lazySet(0, parent);5}
这个actual就是传入的Runnable对象。
然后executor就开始执行Runnable任务了,即在这个时候进行了线程调度,接下来我们看看SchedulerRunnable的run()都做了什么:
1@Override 2public void run() { 3 lazySet(THREAD_INDEX, Thread.currentThread()); 4 try { 5 try { 6 actual.run(); 7 } catch (Throwable e) { 8 // Exceptions.throwIfFatal(e); nowhere to go 9 RxJavaPlugins.onError(e);10 }11 } finally {12 lazySet(THREAD_INDEX, null);13 Object o = get(PARENT_INDEX);14 if (o != PARENT_DISPOSED && compareAndSet(PARENT_INDEX, o, DONE) && o != null) {15 ((DisposableContainer)o).delete(this);16 }1718 for (;;) {19 o = get(FUTURE_INDEX);20 if (o == SYNC_DISPOSED || o == ASYNC_DISPOSED || compareAndSet(FUTURE_INDEX, o, DONE)) {21 break;22 }23 }24 }25}
实际上执行的是传入的Runnable对象的run()方法,即DisposeTask的run():
1@Override 2public void run() { 3 runner = Thread.currentThread(); 4 try { 5 decoratedRun.run(); 6 } finally { 7 dispose(); 8 runner = null; 9 }10}
在run()里执行的是decoratedRun的run()方法,而这个decoratedRun其实就是最早我们传入的Runnable对象,即SubscribeTask对象,我们再看看SubscribeTask的run()做了什么:
1@Override2public void run() {3 source.subscribe(parent);4}
好了,到这儿已经走到了这个逆向订阅的末端,同时我们终于看到了在scheduleDirect中实际上提交给Worker的任务,就是将上游订阅SubscribeOnObserver对象的任务交由指定的线程进行处理。
所以,subscribeOn()的线程调度,实际上是在逆向订阅的过程中进行的,接下来继续由下游到上游订阅,上游到下游执行任务,在遇到下一次的线程调度之前,将都会在指定的线程中执行。接下来,让我们看看ObserveOn()的执行过程;
3. 源码解析:observeOn()
我们从逆向订阅看起:
1@Override 2protected void subscribeActual(Observer<? super T> observer) { 3 if (scheduler instanceof TrampolineScheduler) { 4 source.subscribe(observer); 5 } else { 6 Scheduler.Worker w = scheduler.createWorker(); 7 8 source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize)); 9 }10}
先判断是否为当前线程的Scheduler,如果是,什么都不做直接订阅;如果不是,就创建一个Worker对象,然后进行上游订阅的操作。Worker的原理不再赘述。所以我们发现,在ObserveOn()的逆向订阅过程中,只是创建了对象,但并没有进行线程调度,那么我们可以推测,线程调度的时机,应该是在最后由上至下的任务执行阶段,实际上也确实如此,我们直接看onNext():
1@Override 2public void onNext(T t) { 3 if (done) { 4 return; 5 } 6 7 if (sourceMode != QueueDisposable.ASYNC) { 8 queue.offer(t); 9 }10 schedule();11}121314void schedule() {15 if (getAndIncrement() == 0) {16 worker.schedule(this);17 }18}
同subscribeOn()一样,将任务提交给Worker去执行,在这个过程中进行了线程调度。Worker的执行过程不再赘述,这个this其实就是实现了Runnable接口的ObservableObserveOn对象,而在run()方法中:
1@Override2public void run() {3 if (outputFused) {4 drainFused();5 } else {6 drainNormal();7 }8}
这个outputFused变量在两个连续的Observable都需要线程调度时(比如observeOn().observeOn())有为true的可能,默认为false。我们先跳过这个变量,直接看drainNormal()方法:
1void drainNormal() { 2 int missed = 1; 3 4 final SimpleQueue<T> q = queue; 5 final Observer<? super T> a = actual; 6 7 for (;;) { 8 if (checkTerminated(done, q.isEmpty(), a)) { 9 return;10 }1112 for (;;) {13 boolean d = done;14 T v;1516 try {17 v = q.poll();18 } catch (Throwable ex) {19 Exceptions.throwIfFatal(ex);20 s.dispose();21 q.clear();22 a.onError(ex);23 worker.dispose();24 return;25 }26 boolean empty = v == null;2728 if (checkTerminated(d, empty, a)) {29 return;30 }3132 if (empty) {33 break;34 }3536 a.onNext(v);37 }3839 missed = addAndGet(-missed);40 if (missed == 0) {41 break;42 }43 }44}
这段代码的本质是执行了Observer的onNext()方法,继续往下游。所以observeOn()的线程调度实际上是在任务执行的时候进行的,而接下来上游到下游的任务执行,在遇到下一次用observeOn()进行线程调度之前,将都会在指定的线程中执行。
讲解完subscribeOn()和observeOn()的流程,我们回到示例代码:
4. 示例代码过程说明
我们进行一下分析:
首先在对象创建的过程中,没有线程调度。
在逆向订阅的过程中,subscribeOn()会由下而上起作用:首先会遇到最下面的subscribeOn(Schedulers.single()),然后往上走,doOnSubscribe()指的是在订阅后回调即被执行,所以首先打印出:
然后我们接着往上游走,先遇到subscribeOn(Schedulers.io()),切换至io线程,然后继续往上订阅subscribeOn(Schedulers.newThread()),切换至新线程:再往上走,走到create生成的Observable的订阅流程,至此,逆向订阅的过程完成,开始进行任务执行过程。
首先走到create中的subscribe()方法,这时仍停留在新线程中,打印结果如下:
继续往下走,遇到第一个observeOn(),即observeOn(Schedulers.io()),进行了线程调度,故下游的map1操作执行在该io线程:
继续往下走,遇到第二个observeOn(),即observeOn(Schedulers.newThread()),进行了线程调度,故下游的map2操作执行在该新线程:
继续往下走,遇到第三个observeOn(),即observeOn(Schedulers.mainThread()),进行了线程调度,即subscribe的操作回到主线程:
到此,整个的调度过程也就梳理清楚了。
5. 总结
subscribeOn:通过接收一个Scheduler参数,来指定对数据的处理运行在特定的线程调度器Scheduler上。它影响的是逆向订阅过程中onSubscribe()之后的操作。若多次执行subscribeOn,则只有一次起作用(Observable的创建操作符总是被其之后最近的subscribeOn控制),影响上游线程调度。但是,有一种情况特殊,就是在doOnSubscribe操作符之后调用,可以使doOnSubscribe()在指定的调度器中执行,因为doOnSubscribe()在被订阅后会立即执行,所以它不受上游subscribeOn()的影响。
observeOn:接收一个Scheduler参数,来指定对数据的处理运行在特定的线程调度器Scheduler上。它影响的是任务执行时onNext()之后的操作。若多次执行observeOn,则每次都起作用,线程会一直切换,影响下游线程调度。
狐友技术团队其他精彩文章
Swift之Codable实战技巧
不了解GIF的加载原理?看我就够了!
安卓系统权限,你真的了解吗?
加入搜狐技术作者天团
千元稿费等你来!
戳这里!☛