RxJava 系列九:线程切换的源码分析

1,041 阅读10分钟

本文概述:

  • 此文为RxJava 系列第九篇文章,为本系列难度最高,撰写困难最多的一篇;文章以源码分析配合流程图深入细致分析了subscribeOn 原理;Schedulers.io() 到底干了什么?Schedulers 中的策略机制的具体实现;.subscribeOn():干了什么;任务与线程池是怎么关联起来的;ObservableObserveOn 源码分析

零碎问题

rxandroid 与 rxjava有什么区别:为什么要导入rxandroid

  • 依赖导入示意

    image-20220703142135694

  • 只有RxJava 没有rxandroid是不全的:在android 中需要使用,两个都要导入进来

  • rxandroid 有什么用:在线程切换时有用

     .observeOn(Schedulers.io()) // rxjava
     .subscribeOn(AndroidSchedulers.mainThread()) // rxandroid
    
    • 源码示意:确实是从rxandroid 中导入的

      image-20220703142842172

  • 在 Java 服务器上就没有必要导入 rxandroid 了

中断回收

  • 为什么要使用中断:不添加这个代码会报黄

    • 报黄示意:

      image-20220703143432160

    • 处理报黄:消极手段(在函数签名前添加注解,进行镇压)

    • 处理报黄:因为没有进行中断处理,那么接收中断

      • 实例化中断对象
       private Disposable disposable;
      
      • 接收中断信号

      图片.png

  • 在订阅函数内对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

基本代码准备:

  • 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 中的策略机制的具体实现

  • 结果:实例化具体的策略,最终通过线程池管控,但此时没有触发

  • 示意图:

    Schedulers.io

  • 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)

        • 一定是交给线程池运行的

        image-20220703154215523

       //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() {
 ​
             }
         });
 }
  • 运行结果:整块代码都是在子线程

    result

  • 业务需求:终点处变成主线程

    • 添加代码即可解决

       .observeOn(AndroidSchedulers.mainThread())
      

为什么添加了上面哪行代码就切到了主线程中去了?

  • 最终效果:

    • 拿到了一个百分百在主线程中运行的Handler 暴露了任务入口run ,我们只需要将任务交给Runnable 交给run ,那么这部分代码就运行在主线程中了
  • 源码分析:

    • 整体流程:
    • image-20220704002947780

    • 细节分析:AndroidSchedulers.MainHolder

      • Handler 切到主线程

        image-20220704000832660

    • 细节分析:HandlerScheduler

      • createWorker最终执行到schedule

        image-20220704001630862

      • 为什么会执行到Schedule?

        • Scheduler.createWoker是个抽象函数

          image-20220704001822622

        • 具体实现中会调用 w.schedule();

          image-20220704001921703

    • 细节分析:HandlerScheduler.HandlerWorker.schedule

      • 此时使用的就是主线程的 Handler

        image-20220704002127593

      • 通过run 执行到主线程中去

        image-20220704002233549

      • run 中包裹的代码就在主线程中执行

    • 细节分析:AndroidSchedulers.MAIN_THREAD

      • Hook 技术 + 使用Callable + 返回了MainHolder.DEFAULT;

        image-20220704000714009

源码分析:ObservableObserveOn

  • 画图:new Thread 运行的全部都在子线程中运行

    image-20220704003857647

  • 部分源码分析:ObservableObserveOn

    • 存放了一份主线程的Handler

    image-20220704003610697

    • 节省开销:切换的目标线程是当前线程,那么就不切换,直接调用

    image-20220704003624171

    • 打包:切换的目标线程不是当前线程

      image-20220704003717191

  • 源码详细分析过程:

    • 事件终点处发起订阅,订阅中存在抽象subscribeActual(),跳转到实现处

      • 订阅函数的调用者:ObservableObserveOn
      • 这是由Rx 响应式编程决定的
    • 跳转至ObservableObserveOn.subscribeActual(事件终点)

      • 封装包裹一并向上传递:注意此时source 指的是再上面的一层

        image-20220704004431574

    • 跳转至ObservableCreate.subscribeActual(),抽象函数在上层调用者中实现

      • 接收入参(包裹一) + 封装包裹二

      图片.png

    • 跳转到自定义source:开始向下依次分发(调用各层的onNext() 方法)

      • 开始向下分发:包裹二.onNext()

        image-20220704005031347

    • 重点:包裹一.onNext(),此时需要将任务提交给Handler

      • 此时还是在子线程中,但执行了schedule 之后就交给了Handler,就切到主线程了

        image-20220704005420732

    • schedule 中干了什么:分析ObservableObserveOn中重写的onNext()

      • 完成了线程的切换与任务的提交

      • 拿到worker

        image-20220704010745655

      • worker 是哪里来的:分析ObservableObserveOn.subscribeActual()

        • scheduler 就是之前返回的主线程 Handler

          • 意味着scheduler 可以在Handler中执行主线程代码

        image-20220704010915936

      • 因为worker 是 Scheduler.Worker 类型

        image-20220704010422361

      • 调用抽象函数createWorker();

        image-20220704010240496

      • 实现类HandlerScheduler,返回HandlerWorker(handler)对象

        图片.png

      • 最终调用schedule:ObservableObserveOn.schedule

        image-20220704011242358

      • 跳转到HandlerScheduler 中重写的schedule

        • 将任务以Runnable 形式传进来

        image-20220704010135355

        • 提交给Handler

        image-20220704010152025

        • 此时意味着 run 函数就在主线程中了

          • 最后是由HandlerScheduler.run 函数执行的
          • run 函数中均属于主线程(android.main)部分
          • 拿到事件终点后:事件终点.onNext()
    • 源码分析(ObservableObserveOn 重写的run()):run 函数干了什么?

      • drainFused();

      • drainNormal();

        • 拿到事件流动终点

          image-20220704012334727

        • 终点.onNext():就切到主线程了

          image-20220704012422713

  • 补充: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 的赋值位置:只有一处

    image-20220703145031008

  • Hook 思路:我们只需要调用这个函数,对 f 进行赋值即可(后续在执行的时候,先执行Hook 代码),呈现出全局监听的效果(因为这个是静态的)

    • 想怎么玩就怎么玩

    image-20220703145241918

  • 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);