title: RxJava2源码解析(二)
categories:

  • 源码解析
    tags:
  • 源码解析
  • rxJava2

前言

本篇主要解析RxJava的线程切换的原理实现

subscribeOn

首先, 我们先看下subscribeOn()方法, 老样子, 先上Demo

Observable<Integer> observable =
Observable
.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(123);
emitter.onComplete();
}
}); observable
.subscribeOn(Schedulers.io())
.subscribe(getObserver());
复制代码

subscribeOn操作符源码里其实是返回了一个ObservableSubscribeOn对象, 而从上篇我们已经知道, 订阅的动作其实在每个ObservablesubscribeActual(observer)中执行, 所以我们直接去看ObservableSubscribeOn中的对应重载方法就行了.

@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 SubscribeTask(parent)));
}
复制代码
final class SubscribeTask implements Runnable {
private final SubscribeOnObserver<T> parent; SubscribeTask(SubscribeOnObserver<T> parent) {
this.parent = parent;
} @Override
public void run() {
source.subscribe(parent);
}
}
复制代码

SubscribeTask是一个Runnable的实现类, 执行内容就是修饰后的Observer订阅上游的动作, 我们先看scheduler.scheduleDirect(runable)方法

@NonNull
public Disposable scheduleDirect(@NonNull Runnable run) {
return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);
} @NonNull
public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
final Worker w = createWorker(); final Runnable decoratedRun = RxJavaPlugins.onSchedule(run); DisposeTask task = new DisposeTask(decoratedRun, w); w.schedule(task, delay, unit); return task;
}
复制代码

这里createWorker是个抽象方法, 我们需要找到对应的修饰类, 我们返回去看Schedulers.io(), IOIoScheduler的实例, 它的重载方法代码如下

final AtomicReference<CachedWorkerPool> pool;
public Worker createWorker() {
return new EventLoopWorker(pool.get());
}
复制代码

可以看到IO线程实际使用的是一个有线程缓存的线程调度器.它内部通过ScheduledExecutorService实例来尝试重用之前worker开始使用的实例, 由于本篇着重在流程实现原理, 所以略过细节处.
EventLoopWorker中, 我们看下对应的重载方法

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);
}
复制代码

继续往下, 其实这个时候已经是在线程池目标线程执行相关的工作了. 再深入就是线程池的操作了, 所以这里我们不再赘述

public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
Runnable decoratedRun = RxJavaPlugins.onSchedule(run); ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent); if (parent != null) {
if (!parent.add(sr)) {
return sr;
}
} 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) {
if (parent != null) {
parent.remove(sr);
}
RxJavaPlugins.onError(ex);
} return sr;
}
复制代码

由此我们可以看出来, 每次每个subscribeOn操作符执行的时候, 其实在source.subscribe(parent);订阅动作就做了线程切换, 所以在多次调subscribeOn的时候, 就会一直切换线程, 直到离ObservableSource最近的subscribeOn线程切换生效.

observeOn

废话不说, 我们直接看ObservableObserveOn.subscribeActual(observer)

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));
}
}
复制代码

熟悉的配方, 当相同想成的时候, 直接订阅, 而当不同线程的时候, 可以看到我们获取目标切换线程对应的worker实例以及装饰对应的ObserverObserveOnOberver,后面的流程我们心知肚明, 就是Observer层层订阅上去, 然后我们看当碰到最上流的ObservableSource往下执行的时候, 做什么操作.具体我们看ObserveOnOberver代码, 我们这里着重看下onSubscribeonNext方法

@Override
public void onSubscribe(Disposable s) {
if (DisposableHelper.validate(this.s, s)) {
this.s = s;
// 发送的数据是集合队列形式的时候
if (s instanceof QueueDisposable) {
@SuppressWarnings("unchecked")
QueueDisposable<T> qd = (QueueDisposable<T>) s; int m = qd.requestFusion(QueueDisposable.ANY | QueueDisposable.BOUNDARY);
//是同步模式的时候
if (m == QueueDisposable.SYNC) {
sourceMode = m;
queue = qd;
done = true;
actual.onSubscribe(this);
// 线程调度
schedule();
return;
}
// 异步模式
if (m == QueueDisposable.ASYNC) {
sourceMode = m;
queue = qd;
actual.onSubscribe(this);
return;
}
} queue = new SpscLinkedArrayQueue<T>(bufferSize); actual.onSubscribe(this);
}
} @Override
public void onNext(T t) {
// 是否已经调用到onComplete 或者 onError, 如果是, 则不再执行后面的onNext
if (done) {
return;
}
// 如果是非异步操作, 将数据添加到队列中
if (sourceMode != QueueDisposable.ASYNC) {
queue.offer(t);
}
// 线程调度
schedule();
}
复制代码

大概的注释都加在代码上了, 我们再补充看下看onSubscribe方法, 首先判断发送的数据是否属于QueueDisposable, 如果不是, 直接执行下游的onSubscribe,这里我卡了一下, 看不到他的线程切换是在哪里做, 后来往回看, 发现在我们执行ObservableSubscribeOn.subscribeActual(observer)的时候, onSubscribe()方法本身的确不是在切换后的线程内执行的. 但是, 当我们发送的是集合数据, 那么我们需要判断是哪种线程模式进行线程调度.

我们来看具体的schedule()方法代码

void schedule() {
// 判断当前自增值是否为0, 原子性保证worker.schedule(this);不会在调用结束前被重复调用
if (getAndIncrement() == 0) {
worker.schedule(this);
}
}
复制代码

这个时候就是在指定线程内run了, Disposable schedule(@NonNull Runnable run)传入的是个Runnable的实现类, 我们来找重载的run方法

@Override
public void run() {
if (outputFused) {
drainFused();
} else {
drainNormal();
}
} void drainNormal() {
int missed = 1; final SimpleQueue<T> q = queue;
final Observer<? super T> a = actual;
// 无限循环
for (;;) {
// 判断是否被取消, 或者调用onError 或者调用onComplete则退出循环
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);
}
// 原子性保证worker.schedule(this)的调用
missed = addAndGet(-missed);
if (missed == 0) {
break;
}
}
} // 判断循环是否终止
boolean checkTerminated(boolean d, boolean empty, Observer<? super T> a) {
// 如果订阅已经被取消, 则清除队列, 终止
if (cancelled) {
queue.clear();
return true;
}
// 如果调用过onError 或者 onComplete
if (d) {
Throwable e = error;
// 默认false
if (delayError) {
// 等到队列为空的时候再调用onError或者onComplete
if (empty) {
if (e != null) {
a.onError(e);
} else {
a.onComplete();
}
worker.dispose();
return true;
}
} else {
// 如果有抛出异常, 走下游的onError
// 线程任务停止
if (e != null) {
queue.clear();
a.onError(e);
worker.dispose();
return true;
}
// 没有, 走下游的onComplete
// 线程任务停止
else if (empty) {
a.onComplete();
worker.dispose();
return true;
}
}
}
// 否则不结束
return false;
}
复制代码

由此我们可以得出结论, observeOn的操作符可以保证我们下流操作线程切换生效

总结

到这里, 我们线程切换的原理大体流程就基本分析完毕了, 可以看出subscribeOn操作符只对上游生效, 而且因为他是在订阅的时候进行线程切换, 而我们每个操作符中间都有订阅动作, 所以越接近我们的ObservableSource的订阅的subscribeOn越是最后生效的. 而observeOn生效在我们的onNext,onComplete, onError方法内, 所以每次的observeOn针对它的下游都可以生效.

RxJava2源码解析(二)的更多相关文章

  1. RxJava2 源码解析(二)

    概述 承接上一篇RxJava2 源码解析(一),本系列我们的目的: 知道源头(Observable)是如何将数据发送出去的.    知道终点(Observer)是如何接收到数据的.    何时将源头和 ...

  2. Android进阶:五、RxJava2源码解析 2

    上一篇文章Android进阶:四.RxJava2 源码解析 1里我们讲到Rxjava2 从创建一个事件到事件被观察的过程原理,这篇文章我们讲Rxjava2中链式调用的原理.本文不讲用法,仍然需要读者熟 ...

  3. Mybatis源码解析(二) —— 加载 Configuration

    Mybatis源码解析(二) -- 加载 Configuration    正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...

  4. Sentinel源码解析二(Slot总览)

    写在前面 本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点.那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(s ...

  5. Android进阶:四、RxJava2 源码解析 1

    本文适合使用过Rxjava2或者了解Rxjava2的基本用法的同学阅读 一.Rxjava是什么 Rxjava在GitHub 主页上的自我介绍是 "a library for composin ...

  6. iOS即时通讯之CocoaAsyncSocket源码解析二

    原文 前言 本文承接上文:iOS即时通讯之CocoaAsyncSocket源码解析一 上文我们提到了GCDAsyncSocket的初始化,以及最终connect之前的准备工作,包括一些错误检查:本机地 ...

  7. jQuery 源码解析二:jQuery.fn.extend=jQuery.extend 方法探究

    终于动笔开始 jQuery 源码解析第二篇,写文章还真是有难度,要把自已懂的表述清楚,要让别人听懂真的不是一见易事. 在 jQuery 源码解析一:jQuery 类库整体架构设计解析 一文,大致描述了 ...

  8. Common.Logging源码解析二

    Common.Logging源码解析一分析了LogManager主入口的整个逻辑,其中第二步生成日志实例工厂类接口分析的很模糊,本随笔将会详细讲解整个日志实例工厂类接口的生成过程! (1).关于如何生 ...

  9. erlang下lists模块sort(排序)方法源码解析(二)

    上接erlang下lists模块sort(排序)方法源码解析(一),到目前为止,list列表已经被分割成N个列表,而且每个列表的元素是有序的(从大到小) 下面我们重点来看看mergel和rmergel ...

随机推荐

  1. Java中的数据结构-HashMap

    Java数据结构-HashMap 目录 Java数据结构-HashMap 1. HashMap 1.1 HashMap介绍 1.1.1 HashMap介绍 1.1.2 HashMap继承图 1.2 H ...

  2. HTTP Session例子

    HTTP协议是“一次性单向”协议.服务端不能主动连接客户端,只能被动等待并答复客户端请求.客户端连接服务端,发出一个HTTP Request,服务端处理请求,并且返回一个HTTP Response给客 ...

  3. 下载安装配置 Scala-2.12.11

    文章更新于:2020-03-24 安装惯例,文件附上链接放在文首. 文件名:scala-2.12.11.tgz 文件大小:19.83 MB 下载链接:https://downloads.lightbe ...

  4. Android 添加键值并上报从驱动到上层

    转载:https://blog.csdn.net/weixin_43854010/article/details/94390803 Android 添加键值并上报从驱动到上层 平台 :RK3288 O ...

  5. mongodb 指令

    db.xxx.stats() 查看表的大小 db.xxx.remove({'endtime':{'$lte':ISODate('2018-10-01')}}) 删除小于等于固定时间的数据. db.us ...

  6. Struts2-学习笔记系列(10)-自定义类型转换

    注意name=user和对应action中的实例名称一致 这些代码是写在HTML文件中的 <s:form action="login"> <s:textfield ...

  7. 关于Python 迭代器和生成器 装饰器

    Python 简介Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比 ...

  8. leetcode c++做题思路和题解(4)——队列的例题和总结

    队列的例题和总结 0. 目录 栈实现队列 队列实现栈 滑动窗口最大值 1. 栈实现队列 FIFO和FILO,相当于+-号,互转都是利用"负负得正"的原理. 官方解答中第二种思路很6 ...

  9. Java的运行时数据存储机制

    原文地址:http://yanwushu.sinaapp.com/java_data_storage/ Java程序在运行时需要为一系列的值或者对象分配内存,这些值都存在什么地方?用什么样的数据结构存 ...

  10. <context:component-scan base-package=""> 与 <context:annotation-config 区别

    <context:component-scan base-package=""> <context:annotation-config (2012-11-16 2 ...