本文概述:
- 此文为RxJava 系列第九篇文章,为本系列难度最高,撰写困难最多的一篇;文章以源码分析配合流程图深入细致分析了subscribeOn 原理;Schedulers.io() 到底干了什么?Schedulers 中的策略机制的具体实现;.subscribeOn():干了什么;任务与线程池是怎么关联起来的;ObservableObserveOn 源码分析
零碎问题
rxandroid 与 rxjava有什么区别:为什么要导入rxandroid
-
依赖导入示意
-
只有RxJava 没有rxandroid是不全的:在android 中需要使用,两个都要导入进来
-
rxandroid 有什么用:在线程切换时有用
.observeOn(Schedulers.io()) // rxjava .subscribeOn(AndroidSchedulers.mainThread()) // rxandroid
-
源码示意:确实是从rxandroid 中导入的
-
-
在 Java 服务器上就没有必要导入 rxandroid 了
中断回收
-
为什么要使用中断:不添加这个代码会报黄
-
报黄示意:
-
处理报黄:消极手段(在函数签名前添加注解,进行镇压)
-
处理报黄:因为没有进行中断处理,那么接收中断
- 实例化中断对象
private Disposable disposable;
-
接收中断信号
-
-
在订阅函数内对disposable 进行赋值
.subscribe(new Observer<String>() { @Override public void onSubscribe(Disposable d) { disposable = d; }
-
在onDestroy 中进行回收:阻止事件继续流动 ---> 解决OOM
@Override protected void onDestroy() { super.onDestroy(); // 最起码的写法 if (disposable != null) if (!disposable.isDisposed()) disposable.dispose(); }
Just 与 Create操作符有什么区别
-
为什么要学Create,不学其他的
- Create 是一个最原始的东西,Just 等其他的都是封装了的
-
Just 内部进行了封装,不用手动调用onNext
//Create 操作符 @Override public void subscribe(ObservableEmitter<String> e) throws Exception { e.onNext("Derry"); e.onNext("A"); e.onComplete(); }
-
Just 内部的封装处理
//new ObservableJust() ---> subscribeActual.run() @Override public void run() { if (get() == START && compareAndSet(START, ON_NEXT)) { observer.onNext(value); if (get() == ON_NEXT) { lazySet(ON_COMPLETE); observer.onComplete(); } } }
subscribeOn 原理分析
整体流程概述:
-
流程关系:
-
将事件终点传给订阅处:.subscribe
-
在订阅处:
- 执行抽象函数subscribeActual,跳转到订阅的具体实现类,也就是订阅函数的实现类
-
订阅函数的实现类:
- 抽象函数重写:以事件终点作为参数
- 将事件终点封装成了包裹一
-
将包裹一作为参数传给任务:跳转到
- 实现了Runnable接口,在其run 方法中将包裹一作为参数,与线程池相关联
-
跳转到 的实现类
- 抽象函数重写:以包裹一作为参数
- 封装了包裹二
-
跳转到 的调用者
- 包裹二.onNext()
-
-
示意图:
基本代码准备:
-
subscribeOn 作用:为上面代码分配线程
-
代码展示:onCreate 中
Observable.create( // 自定义source new ObservableOnSubscribe<String>() { @Override public void subscribe(ObservableEmitter<String> e) throws Exception { e.onNext("Derry"); Log.d(L.TAG, "自定义source: " + Thread.currentThread().getName()); } }) // ObservbaleCreate.subscribeOn // TODO 第二步 new IOScheduler ---> 线程池 传递进去 .subscribeOn( // TODO 第一步 到底干了什么 ( new IOScheduler ---> 线程池) Schedulers.io() // 耗时读取的异步 // Schedulers.newThread() // 开启新线程 ) // A线程. subscribe // ObservableSubscribeOn.subscribe .subscribe( // 终点 new Observer<String>() { @Override public void onSubscribe(Disposable d) { Disposable disposable = d; Log.d(L.TAG, "onSubscribe: " + Thread.currentThread().getName()); } @Override public void onNext(String s) { Log.d(L.TAG, "onNext: " + Thread.currentThread().getName()); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
Schedulers.io() 到底干了什么?
-
最终返回:Schedulers 对象
-
进入源码:Hook 技术 + IO
@NonNull public static Scheduler io() { return RxJavaPlugins.onIoScheduler(IO); }
-
Hook技术:因为f 默认为null,直接就返回回去了
@NonNull public static Scheduler onIoScheduler(@NonNull Scheduler defaultScheduler) { Function<? super Scheduler, ? extends Scheduler> f = onIoHandler; if (f == null) { return defaultScheduler; } return apply(f, defaultScheduler); }
- 补充自定义 Hook 技术
-
final 关键字:查看 IO
public final class Schedulers { …… @NonNull static final Scheduler IO;
-
实例化部分:new IOTask();
static { …… //在实例化的时候,又来一个Hook点 IO = RxJavaPlugins.initIoScheduler(new IOTask()); }
-
-
-
为什么来这么多Hook,向用户提供拦截点
//初始化 IO Hook
@NonNull
public static Scheduler initIoScheduler(@NonNull Callable<Scheduler> defaultScheduler) {
ObjectHelper.requireNonNull(defaultScheduler, "Scheduler Callable can't be null");
Function<? super Callable<Scheduler>, ? extends Scheduler> f = onInitIoHandler;
if (f == null) {
return callRequireNonNull(defaultScheduler);
}
return applyRequireNonNull(f, defaultScheduler);
}
- 用户Hook 思路:对 IO 进行初始化
// TODO hook 给 IO 初始化
RxJavaPlugins.setInitIoSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
@Override
public Scheduler apply(Callable<Scheduler> schedulerCallable) throws Exception {
Log.d(L.TAG, "apply: 全局 监听 init scheduler:" +schedulerCallable.call());
return schedulerCallable.call();
}
});
Schedulers 中的策略机制的具体实现
-
结果:实例化具体的策略,最终通过线程池管控,但此时没有触发
-
示意图:
-
IO策略、NEW_THREAD 策略、COMPUTATION 策略
-
IO策略:为耗时读取的 IO操作,提供异步线程,采用IOTask() 进行处理(有返回值(IOScheduler)的异步任务)
- IO 策略:
public final class Schedulers { @NonNull static final Scheduler IO;
- IOTask()
IO = RxJavaPlugins.initIoScheduler(new IOTask());
- new IOTask():有返回值的异步任务
static final class IOTask implements Callable<Scheduler> { @Override public Scheduler call() throws Exception { return IoHolder.DEFAULT; } }
- 返回值为:IOScheduler() 对象
static final class IoHolder { static final Scheduler DEFAULT = new IoScheduler(); }
-
NEW_THREAD 策略:为频繁开启线程,提供异步线程
- 类属性
public final class Schedulers { @NonNull static final Scheduler NEW_THREAD;
- 在静态块内初始化
static{ NEW_THREAD = RxJavaPlugins.initNewThreadScheduler(new NewThreadTask()); }
- 创建一个对象:new NewThreadTask()
static final class NewThreadTask implements Callable<Scheduler> { @Override public Scheduler call() throws Exception { return NewThreadHolder.DEFAULT; } }
- 返回:NewThreadScheduler 实例
static final class NewThreadHolder { static final Scheduler DEFAULT = new NewThreadScheduler(); }
-
是怎么调用到线程池的:以IOScheduler() 为例
- 源码展示:IoScheduler
public IoScheduler() { this(WORKER_THREAD_FACTORY); }
- 通过this 指针进入构造函数
//存在工厂设计模式 public IoScheduler(ThreadFactory threadFactory) { //为这个工厂取个名字 this.threadFactory = threadFactory; this.pool = new AtomicReference<CachedWorkerPool>(NONE); start(); }
- 进入start() 方法:其中做了许多的优化(缓存池)
@Override public void start() { CachedWorkerPool update = new CachedWorkerPool(KEEP_ALIVE_TIME, KEEP_ALIVE_UNIT, threadFactory); if (!pool.compareAndSet(NONE, update)) { update.shutdown(); } }
- 进入shutdown:找到所使用的线程池实例
void shutdown() { allWorkers.dispose(); if (evictorTask != null) { evictorTask.cancel(true); } //就是这个evictorService if (evictorService != null) { evictorService.shutdownNow(); } }
- 证明这个东西确实是线程池
static final class CachedWorkerPool implements Runnable { …… private final ScheduledExecutorService evictorService;
-
因为这个存在继承关系
ScheduledExecutorService extends ExecutorService
-
线程池铁证:
if (unit != null) { //在这个地方 evictor = Executors.newScheduledThreadPool(1, EVICTOR_THREAD_FACTORY); task = evictor.scheduleWithFixedDelay(this, this.keepAliveTime, this.keepAliveTime, TimeUnit.NANOSECONDS); } evictorService = evictor;
.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));
}
- 进入new ObservableSubscribeOn
//入参scheduler:具体策略
public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {
super(source);
this.scheduler = scheduler;
}
-
订阅流程:而订阅是由Schedulers.io() 返回的对象 ObservableSubscribeOn 调用的
-
传入事件终点
-
进行校验
-
调用抽象函数,跳转到其实现类(ObservableSubscribeOn )的具体实现处
-
传入了事件终点 s,打包
-
将包裹交给new SubscribeTask(parent)
- 一定是交给线程池运行的
//s 是事件的终点 @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))); }
-
-
任务与线程池是怎么关联起来的
-
关键代码:ObservableSubscribeOn.subscribeActual()
//c(线程池点出来的) scheduler.scheduleDirect(new SubscribeTask(parent))
-
观察是怎么将任务提交到线程池的:进入scheduleDirect
public Disposable scheduleDirect(@NonNull Runnable run) { return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS); }
- 存在继承关系:父类(Scheduler) ---> 子类(IoScheduler)
-
查看具体实现:进入scheduleDirect
@NonNull public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) { //交给子类去执行:避免破坏设计模式 final Worker w = createWorker(); //又是Hook 技术:将Runnable包装一层 final Runnable decoratedRun = RxJavaPlugins.onSchedule(run); //又将Runnable包装一层 DisposeTask task = new DisposeTask(decoratedRun, w); //将包装好的Runnable交给w ---> 最终调用到EventLoopWorker.schedule w.schedule(task, delay, unit); return task; }
-
观察子类的具体实现逻辑:进入createWorker
-
发现这是一个抽象函数:肯定了上述猜想,一定是交给子类实现的
@NonNull public abstract Worker createWorker();
-
-
查看子类实现:进入 IoScheduler.reateWorker()
- 创建了对象EventLoopWorker
public Worker createWorker() { return new EventLoopWorker(pool.get()); }
-
最终调用到线程池中:NewThreadWorker.scheduleActual()
if (delayTime <= 0) { f = executor.submit((Callable<Object>)sr); } else { f = executor.schedule((Callable<Object>)sr, delayTime, unit); } sr.setFuture(f);
-
为什么使用submit 不用excute
- 都是执行具体任务,在于同步/异步,是否有返回值
-
-
线程池实现了分配线程::ObservableSubscribeOn.SubscribeTask()
final class SubscribeTask implements Runnable { …… @Override public void run() { //线程池执行任务 source.subscribe(parent); } }
- 因为采用线程池 ---> 此处为异步线程,并且将任务提交后,后续流程会立即触发,因此后面的均处在异步线程中了,事件终点都是异步;
- 可以在不同部分,打印线程名字
- 重点:订阅处的onSubscribe 是不参与上述的流程的,打印出来的是subscribe的调用者,此处为android主线程
// 终点 new Observer<String>() { @Override public void onSubscribe(Disposable d) { Disposable disposable = d; Log.d(L.TAG, "onSubscribe: " + Thread.currentThread().getName()); }
终点是需要更新UI:分析observeOn(为下面的代码分配主线程)
- 结果:拿到主线程的Handler,交给observeOn
- 业务需求:现在终点是异步线程,怎么将它变成主线程;
案例分析:
- 基础环境搭建:营造子线程环境:整个链条全部都是子线程
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//将所有代码全部放到子线程中去
new Thread(){
@Override
public void run() {
super.run();
//封装了操作
test();
}
}.start();
}
private void test() {
Observable.create(
// 自定义source
new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
e.onNext("Derry");
Log.d(L.TAG, "自定义source: " + Thread.currentThread().getName());
}
})
//子线程.subscribe
.subscribe(
// 终点
new Observer<String>() {
@Override
public void onSubscribe(Disposable d) {
Log.d(L.TAG, "onSubscribe: " + Thread.currentThread().getName());
}
@Override
public void onNext(String s) {
Log.d(L.TAG, "onNext: " + Thread.currentThread().getName());
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
-
运行结果:整块代码都是在子线程
-
业务需求:终点处变成主线程
-
添加代码即可解决
.observeOn(AndroidSchedulers.mainThread())
-
为什么添加了上面哪行代码就切到了主线程中去了?
-
最终效果:
- 拿到了一个百分百在主线程中运行的Handler 暴露了任务入口run ,我们只需要将任务交给Runnable 交给run ,那么这部分代码就运行在主线程中了
-
源码分析:
- 整体流程:
-
细节分析:AndroidSchedulers.MainHolder
-
Handler 切到主线程
-
-
细节分析:HandlerScheduler
-
createWorker最终执行到schedule
-
为什么会执行到Schedule?
-
Scheduler.createWoker是个抽象函数
-
具体实现中会调用 w.schedule();
-
-
-
细节分析:HandlerScheduler.HandlerWorker.schedule
-
此时使用的就是主线程的 Handler
-
通过run 执行到主线程中去
-
run 中包裹的代码就在主线程中执行
-
-
细节分析:AndroidSchedulers.MAIN_THREAD
-
Hook 技术 + 使用Callable + 返回了MainHolder.DEFAULT;
-
源码分析:ObservableObserveOn
-
画图:new Thread 运行的全部都在子线程中运行
-
部分源码分析:ObservableObserveOn
- 存放了一份主线程的Handler
- 节省开销:切换的目标线程是当前线程,那么就不切换,直接调用
-
打包:切换的目标线程不是当前线程
-
源码详细分析过程:
-
事件终点处发起订阅,订阅中存在抽象subscribeActual(),跳转到实现处
- 订阅函数的调用者:ObservableObserveOn
- 这是由Rx 响应式编程决定的
-
跳转至ObservableObserveOn.subscribeActual(事件终点)
-
封装包裹一并向上传递:注意此时source 指的是再上面的一层
-
-
跳转至ObservableCreate.subscribeActual(),抽象函数在上层调用者中实现
-
接收入参(包裹一) + 封装包裹二
-
-
跳转到自定义source:开始向下依次分发(调用各层的onNext() 方法)
-
开始向下分发:包裹二.onNext()
-
-
重点:包裹一.onNext(),此时需要将任务提交给Handler
-
此时还是在子线程中,但执行了schedule 之后就交给了Handler,就切到主线程了
-
-
schedule 中干了什么:分析ObservableObserveOn中重写的onNext()
-
完成了线程的切换与任务的提交
-
拿到worker
-
worker 是哪里来的:分析ObservableObserveOn.subscribeActual()
-
scheduler 就是之前返回的主线程 Handler
- 意味着scheduler 可以在Handler中执行主线程代码
-
-
因为worker 是 Scheduler.Worker 类型
-
调用抽象函数createWorker();
-
实现类HandlerScheduler,返回HandlerWorker(handler)对象
-
最终调用schedule:ObservableObserveOn.schedule
-
跳转到HandlerScheduler 中重写的schedule
- 将任务以Runnable 形式传进来
- 提交给Handler
-
此时意味着 run 函数就在主线程中了
- 最后是由HandlerScheduler.run 函数执行的
- run 函数中均属于主线程(android.main)部分
- 拿到事件终点后:事件终点.onNext()
-
-
源码分析(ObservableObserveOn 重写的run()):run 函数干了什么?
-
drainFused();
-
drainNormal();
-
拿到事件流动终点
-
终点.onNext():就切到主线程了
-
-
-
-
补充:Handler切换主线程时,为什么要加Looper.getMainLooper()
-
到底为什么?
- 因为一个线程对应一个Loop,main 线程就只有一个Loop
-
最正宗的写法 (百分百在主线程) :传入Looper.getMainLooper()
new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); // UI 操作 ... } };
-
一般写法:80% 可能性在主线程
new Handler() { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); // UI 操作 ... } };
-
最次写法:20% 可能性在主线程
new Thread(){ @Override public void run() { super.run(); new Handler() { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); // UI 操作 ... 百分之百 Ui线程 没有问题 } }; } }.start();
-
补充:自定义Hook 技术(RxJava 2.0)
-
RxJava 1.0:传进来返回回去,只是预留了 2.0 的优化空间
-
寻找 f 的赋值位置:只有一处
-
Hook 思路:我们只需要调用这个函数,对 f 进行赋值即可(后续在执行的时候,先执行Hook 代码),呈现出全局监听的效果(因为这个是静态的)
- 想怎么玩就怎么玩
-
Hook 代码:
// TODO Hook IO 正式干活的 Hook RxJavaPlugins.setIoSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { Log.d(L.TAG, "apply: 全局 监听 scheduler:" +scheduler); return scheduler; } });
异步事件流编程:
- 卡片式编程的线程分配:在上一张卡片切主线,下一张卡片切异步线程
问题汇总:
-
事件终点是什么:流程的走向问题
-
案例一:终点为1
subscribeOn(1); subscribeOn(2); subscribeOn(3);
-
案例二:终点为C
observeOn(A); observeOn(B); observeOn(C);
-