RxJava/RxAndroid source code usage understanding

easy to understand

  Rx is Reactive Extensions, and RxJava is the implementation of responsive extensions in Java.
  It mainly solves the complex problem of interface callback nested reading through the observer mode, and realizes the characteristics of concise and easy-to-read chain calls. It also implements threads suitable for different tasks, such as calculation type, IO type, and common type, which are convenient for users to choose.
  Take a look at how RxJava is used in general, examples are as follows:

Observable.create(emit -> {
    
                
            Thread.sleep(5000);
            emit.onNext("1");            
        })
            .subscribeOn(Schedulers.newThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<Object>() {
    
    

                @Override
                public void accept(Object o) throws Throwable {
    
    
                    Log.e("MainActivity", "接收到数据:" + o.toString());
                }
            });

  As above, the Lambda expression in create is generally a time-consuming operation and cannot be placed in the main thread. At this time, subscribeOn is used to specify the corresponding thread scheduler. This piece is now Schedulers.newThread(), which uses ordinary threads.
   observeOn is also the specified thread scheduler, here is AndroidSchedulers.mainThread(), which means to use the main thread to process the corresponding tasks. Which task does it handle, the subscribe subscription task.
  Let's analyze its principle based on the above code.

related class

  Let's get to know the key classes first, as follows:
  Observable: the class of the observed, which will be inherited by the observed. When it subscribes to an observer, it will call the subscribe(@NonNull Observer<? super T> observer) method, but this method is declared final in the Observable class, so subclasses cannot override this method, and should override subscribeActual(@ NonNull Observer<? super T> observer) method, which will be called in the subscribe() method. There are also many operator methods declared in the Observable class.
  Observer: Observer class interface, observers will inherit this class.
  ObservableOnSubscribe: It is an interface, which has a method to be implemented subscribe (@NonNull ObservableEmitter emitter). Note that its parameter type is the ObservableEmitter interface type, and it implements the Emitter interface. The Emitter interface has three methods, onNext(@NonNull T value), onError(@NonNull Throwable error), onComplete(). ObservableOnSubscribe corresponds to the Lambda expression in the create() method in the above example, and what it implements is its subscribe (@NonNull ObservableEmitter emitter). Although it does not inherit Observable, it should be classified as the observed.
  ObservableCreate: Inherit Observable and belong to the observed. It is generated by the Observable.create() method, in this case, its member source is the ObservableOnSubscribe object.
  ObservableSubscribeOn: inherits Observable and belongs to the observed. In the example, it is generated by calling subscribeOn(), which encapsulates the ObservableCreate object and Schedulers.newThread() into its member variables.
  ObservableObserveOn: inherits Observable and belongs to the observed. In the example, it is generated by calling observeOn(), and its member variables also include ObservableSubscribeOn object, AndroidSchedulers.mainThread() scheduler.
  Through the chain calls of the above class objects, we found that the observed objects generated later always contain the previous observed objects, which actually uses the combined design method.
  LambdaObserver: inherits the Observer object and belongs to the observer. The last step of the chain in the example calls the subscribe() method, and the parameter is the implemented Consumer interface. In fact, the Consumer object will eventually be encapsulated into the LambdaObserver object. The LambdaObserver object has 4 member variables, 1. Consumer<? super T> onNext, 2. Consumer<? super Throwable> onError, 3. Action onComplete, 4. Consumer<? super Disposable> onSubscribe. Among them, onNext is the Consumer interface object in the example.
  SubscribeOnObserver: inherits Observer, which is an observer class. It is generated in the subscription method subscribeActual() in the ObservableSubscribeOn class, and its member variable downstream also points to an observer object.
  NewThreadScheduler: ordinary thread scheduler
  HandlerScheduler: Android main thread scheduler
  ComputationScheduler: Computational task scheduler
  IoScheduler: IO type task scheduler
  NewThreadWorker: It maintains a thread pool, and thread execution is through it.
  Scheduler.Worker: It is a layer of encapsulation, which contains members of NewThreadWorker or its subclasses, through which tasks are delivered.

code flow

  According to the code of the example, look at the whole process
flow chart

flow chart

  Look at the class structure combination reference diagram again

Class Structure Composition Diagram

Class Structure Combination Reference Diagram

  As can be seen from the above figure, each class object contains the object in front of it.
  Let's combine the code to understand the above process.

1. Create an ObservableCreate class object

    @CheckReturnValue
    @NonNull
    @SchedulerSupport(SchedulerSupport.NONE)
    public static <@NonNull T> Observable<T> create(@NonNull ObservableOnSubscribe<T> source) {
    
    
        Objects.requireNonNull(source, "source is null");
        return RxJavaPlugins.onAssembly(new ObservableCreate<>(source));
    }

  It can be seen that the source of the ObservableOnSubscribe class object is set to the member variable of the newly created ObservableCreate object.
  It also calls the RxJavaPlugins.onAssembly() function,

    @NonNull
    public static <@NonNull T> Observable<T> onAssembly(@NonNull Observable<T> source) {
    
    
        Function<? super Observable, ? extends Observable> f = onObservableAssembly;
        if (f != null) {
    
    
            return apply(f, source);
        }
        return source;
    }
    ……
    @NonNull
    static <@NonNull T, @NonNull R> R apply(@NonNull Function<T, R> f, @NonNull T t) {
    
    
        try {
    
    
            return f.apply(t);
        } catch (Throwable ex) {
    
    
            throw ExceptionHelper.wrapOrThrow(ex);
        }
    }

  It can be seen that if the static member onObservableAssembly of the RxJavaPlugins class is set, it will be called to process the newly generated ObservableCreate object. And if you set onObservableAssembly here, you need to use setOnObservableAssembly() of the RxJavaPlugins class, which we have not set up so far. There are many other similar member functions in the RxJavaPlugins class, which are called hook functions here. It is to allow the user to do some processing, and the user can implement it.

2. Create an ObservableSubscribeOn class object

  Then you need to call subscribeOn(@NonNull Scheduler scheduler) of ObservableCreate, but it does not implement this method, and its implementation is in the Observable class

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

  It can be seen that the constructor of ObservableSubscribeOn is also called, where the ObservableCreate object and Scheduler object are set to the member variables of the ObservableSubscribeOn class object.
  Before this step, the Scheduler object will be created first, or take the Schedulers.newThread() in the previous example, look at its code:

    @NonNull
    static final Scheduler NEW_THREAD; 
    …………   
    static {
    
    
    …………
        NEW_THREAD = RxJavaPlugins.initNewThreadScheduler(new NewThreadTask());
    } 
    …………       
    @NonNull
    public static Scheduler newThread() {
    
    
        return RxJavaPlugins.onNewThreadScheduler(NEW_THREAD);
    }

  It can be seen that NEW_THREAD will be created when the Schedulers class is loaded, and NEW_THREAD will be returned every time newThread() is called. RxJavaPlugins.onNewThreadScheduler() is also a hook function, which is not set here.
  Then look at the creation of NEW_THREAD,

    static final class NewThreadTask implements Supplier<Scheduler> {
    
    
        @Override
        public Scheduler get() {
    
    
            return NewThreadHolder.DEFAULT;
        }
    }
    …………
    static final class NewThreadHolder {
    
    
        static final Scheduler DEFAULT = new NewThreadScheduler();
    }    

  NewThreadHolder.DEFAULT generates objects through the constructor of the NewThreadScheduler class.
  In this way, calling Schedulers.newThread() in the future will directly return the NewThreadHolder.DEFAULT object.

3. Create an ObservableObserveOn class object

  You should now call the observeOn() method of ObservableSubscribeOn, which is implemented in the Observable class:

    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.CUSTOM)
    @NonNull
    public final Observable<T> observeOn(@NonNull Scheduler scheduler) {
    
    
        return observeOn(scheduler, false, bufferSize());
    }
    …………
    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.CUSTOM)
    @NonNull
    public final Observable<T> observeOn(@NonNull Scheduler scheduler, boolean delayError, int bufferSize) {
    
    
        Objects.requireNonNull(scheduler, "scheduler is null");
        ObjectHelper.verifyPositive(bufferSize, "bufferSize");
        return RxJavaPlugins.onAssembly(new ObservableObserveOn<>(this, scheduler, delayError, bufferSize));
    }    

  The code is also similar to the above, generating an ObservableObserveOn object, encapsulating the ObservableSubscribeOn object, Scheduler object, delayError, bufferSize as parameters into the ObservableObserveOn object. bufferSize is a reference value for the size of the single producer consumer queue implemented in it, here the size is 128.   Before this step, the Scheduler object will be created first, or AndroidSchedulers.mainThread()
  in the previous example

    private static final class MainHolder {
    
    
        static final Scheduler DEFAULT = internalFrom(Looper.getMainLooper(), true);
    }    
    private static final Scheduler MAIN_THREAD =
        RxAndroidPlugins.initMainThreadScheduler(() -> MainHolder.DEFAULT);
        …………
    public static Scheduler mainThread() {
    
    
        return RxAndroidPlugins.onMainThreadScheduler(MAIN_THREAD);
    }

  This method also gets the static variable MAIN_THREAD, which is obtained through the internalFrom() method

    @SuppressLint("NewApi") // Checking for an @hide API.
    private static Scheduler internalFrom(Looper looper, boolean async) {
    
    
        // Below code exists in androidx-core as well, but is left here rather than include an
        // entire extra dependency.
        // https://developer.android.com/reference/kotlin/androidx/core/os/MessageCompat?hl=en#setAsynchronous(android.os.Message,%20kotlin.Boolean)
        if (Build.VERSION.SDK_INT < 16) {
    
    
            async = false;
        } else if (async && Build.VERSION.SDK_INT < 22) {
    
    
            // Confirm that the method is available on this API level despite being @hide.
            Message message = Message.obtain();
            try {
    
    
                message.setAsynchronous(true);
            } catch (NoSuchMethodError e) {
    
    
                async = false;
            }
            message.recycle();
        }
        return new HandlerScheduler(new Handler(looper), async);
    }

  The HandlerScheduler object is created here, which encapsulates the Handler object and the async parameter, which represents whether the sent message is an asynchronous message, and is adapted according to the system version.
  The looper of the Handler generated here is Looper.getMainLooper(), which means that the Handler processes the received messages in the main thread.
  In this way, the ObservableObserveOn object is generated through chain layer combination.
  Two task schedulers are specified through steps 2 and 3. Time-consuming operations in Android generally cannot be executed in the main thread, so the second step of task scheduling is to put time-consuming operations in a new thread for execution. After the time-consuming result is executed, the execution result will be returned to the main thread, so the third step is to switch back to the main thread to process the corresponding result.
  Then look down, the specific thread scheduling, which is implemented in the subscribe() method.

4. Subscribe to observers

  In the example, a Consumer object is subscribed, and eventually it will call the following method in the Observable class file:

    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    @NonNull
    public final Disposable subscribe(@NonNull Consumer<? super T> onNext) {
    
    
        return subscribe(onNext, Functions.ON_ERROR_MISSING, Functions.EMPTY_ACTION);
    }    
    …………
    @CheckReturnValue
    @SchedulerSupport(SchedulerSupport.NONE)
    @NonNull
    public final Disposable subscribe(@NonNull Consumer<? super T> onNext, @NonNull Consumer<? super Throwable> onError,
            @NonNull Action onComplete) {
    
    
        Objects.requireNonNull(onNext, "onNext is null");
        Objects.requireNonNull(onError, "onError is null");
        Objects.requireNonNull(onComplete, "onComplete is null");

        LambdaObserver<T> ls = new LambdaObserver<>(onNext, onError, onComplete, Functions.emptyConsumer());

        subscribe(ls);

        return ls;
    }

  You can see that the Consumer object is encapsulated into LambdaObserver, and the member variable onError of the LambdaObserver object is set to Functions.ON_ERROR_MISSING, onComplete is set to Functions.EMPTY_ACTION, onSubscribe is set to Functions.emptyConsumer(), and then subscribe(ls) is called .

    @SchedulerSupport(SchedulerSupport.NONE)
    @Override
    public final void subscribe(@NonNull Observer<? super T> observer) {
    
    
        Objects.requireNonNull(observer, "observer is null");
        try {
    
    
            observer = RxJavaPlugins.onSubscribe(this, observer);

            Objects.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null Observer. Please change the handler provided to RxJavaPlugins.setOnObservableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins");

            subscribeActual(observer);
        } catch (NullPointerException e) {
    
     // NOPMD
            throw e;
        } catch (Throwable e) {
    
    
            Exceptions.throwIfFatal(e);
            // can't call onError because no way to know if a Disposable has been set or not
            // can't call onSubscribe because the call might have set a Subscription already
            RxJavaPlugins.onError(e);

            NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");
            npe.initCause(e);
            throw npe;
        }
    }

  He will call the hook function RxJavaPlugins.onSubscribe(), but this needs to be set, currently there is no setting status, continue to call subscribeActual(observer), we know that the current specific instance type is an ObservableObserveOn object, let's take a look at the subscribeActual(observer) method of this class :

    @Override
    protected void subscribeActual(Observer<? super T> observer) {
    
    
        if (scheduler instanceof TrampolineScheduler) {
    
    
            source.subscribe(observer);
        } else {
    
    
            Scheduler.Worker w = scheduler.createWorker();

            source.subscribe(new ObserveOnObserver<>(observer, w, delayError, bufferSize));
        }
    }

  Since the current scheduler is not a TrampolineScheduler type, it will call scheduler.createWorker() to create a Worker object, and then encapsulate it into an ObserveOnObserver object. In this example, the scheduler is a HandlerScheduler object, and the observer is a LambdaObserver object. The source is an ObservableSubscribeOn object. As mentioned earlier, the subscribe() method of the observed object will eventually call the subscribeActual() of the ObservableSubscribeOn class. Take a look at its code:

    @Override
    public void subscribeActual(final Observer<? super T> observer) {
    
    
        final SubscribeOnObserver<T> parent = new SubscribeOnObserver<>(observer);

        observer.onSubscribe(parent);

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

  Here, first encapsulate the parameters into the SubscribeOnObserver object, then call the observer.onSubscribe(parent) of the parameter, then encapsulate the SubscribeOnObserver object just generated into the SubscribeTask object, then call the scheduleDirect() of the thread scheduler object, and finally scheduleDirect( ) is set to the SubscribeOnObserver object.

ObserveOnObserver is subscribed

  Here observer is the ObserveOnObserver object, look at its onSubscribe(parent)

        @Override
        public void onSubscribe(Disposable d) {
    
    
            if (DisposableHelper.validate(this.upstream, d)) {
    
    
                this.upstream = d;
                …………
                queue = new SpscLinkedArrayQueue<>(bufferSize);

                downstream.onSubscribe(this);
            }
        }

  Here upstream is a reference to the Disposable class, which is null at the beginning, and DisposableHelper.validate(this.upstream, d) returns true when judging that upstream is null. Then set this.upstream as the parameter d, where d is the SubscribeOnObserver object. In fact, if this.upstream is not null, DisposableHelper.validate() will call the reportDisposableSet() method, which will throw an exception ProtocolViolationException ("Disposable already set!"), which shows that an observer cannot be subscribed multiple times.
  Then generate the SpscLinkedArrayQueue object, which is a single producer single consumer queue. Then call downstream's onSubscribe(), where downstream is a LambdaObserver object, and then look at its onSubscribe()

LambdaObserver is subscribed

    @Override
    public void onSubscribe(Disposable d) {
    
    
        if (DisposableHelper.setOnce(this, d)) {
    
    
            try {
    
    
                onSubscribe.accept(this);
            } catch (Throwable ex) {
    
    
                Exceptions.throwIfFatal(ex);
                d.dispose();
                onError(ex);
            }
        }
    }

  Here the parameter is the ObserveOnObserver object, LambdaObserver inherits AtomicReference, DisposableHelper.setOnce(this, d) checks if it has not been set, it will set it to d, and return true. Then onSubscribe.accept(this) will be called, where onSubscribe, when constructing a LambdaObserver object through a signature, onSubscribe is Functions.emptyConsumer(), which is an empty implementation.

Scheduler schedules threads

  Return to subscribeActual() of the ObservableSubscribeOn class, after executing the onSubscribe(parent) of ObserveOnObserver, you need to execute scheduler.scheduleDirect(). scheduler is a NewThreadScheduler object. But the scheduleDirect() method is implemented in the Schduler class.

    @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;
    }

  Call Schduler the thread scheduler, and the key code is here.
  1. Call createWorker() to create a Worker object.
  2. Encapsulate the Runnable object decoratedRun and the Worker object w into a DisposeTask object.
  3. Call the schedule() method of the Worker object.

Create a Worker object

  createWorker() is an abstract function in the Schduler class, and its implementation is in subclasses. Since the scheduler is a NewThreadScheduler object, look at its implementation

    @NonNull
    @Override
    public Worker createWorker() {
    
    
        return new NewThreadWorker(threadFactory);
    }

  threadFactory is a static variable which is the thread factory. The priority of the created thread, if not set in the corresponding property, it is Thread.NORM_PRIORITY. It can be seen that the threads generated by the NewThreadScheduler scheduler are all ordinary threads.
  There is a member variable executor in the NewThreadWorker object, which is ScheduledExecutorService, the actual type is ScheduledThreadPoolExecutor, a thread pool.

    public static ScheduledExecutorService create(ThreadFactory factory) {
    
    
        final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1, factory);
        exec.setRemoveOnCancelPolicy(PURGE_ENABLED);
        return exec;
    }
    …………
    …………
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
    
    
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue(), threadFactory);
    }

  Its number of core threads is 1, and the maximum number of threads is Integer.MAX_VALUE. The redundant threads are recycled after 10 ms of idle time after completing tasks, and its storage queue is DelayedWorkQueue. We know that DelayedWorkQueue is a thread that schedules tasks according to the length of the delay, and there is no limit to the size of the task queue, and there is only one core thread, which will cause more tasks to be executed in only one thread.

Scheduling tasks in new threads

  Take a look at the schedule() method of NewThreadWorker

    @NonNull
    @Override
    public Disposable schedule(@NonNull final Runnable action, long delayTime, @NonNull TimeUnit unit) {
    
    
        if (disposed) {
    
    
            return EmptyDisposable.INSTANCE;
        }
        return scheduleActual(action, delayTime, unit, null);
    }
	…………
    @NonNull
    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;
    }

  The parameter run is now a SubscribeTask object, which contains a SubscribeOnObserver object.
  First of all, call RxJavaPlugins.onSchedule(run) to process the run, but there is no corresponding method set here, so it returns itself.
  Then encapsulate the decoratedRun and parent into the ScheduledRunnable object, and the ScheduledRunnable inherits AtomicReferenceArray to implement Runnable and Callable. The length of its AtomicReferenceArray is 3. The parent container is placed at position 0, which is to initialize the incoming parent. Position 1 is the FUTURE position, which is the object returned when the thread pool executes Callable, and position 2 is the thread object that the thread pool runs.
  Then, if the parameter parent parameter is not null, the generated ScheduledRunnable object will be added to the parent container parent.
  Immediately afterwards, the thread pool schedules the corresponding tasks and creates new threads to execute the tasks.
  Finally call setFuture(f) of ScheduledRunnable.

Create a new thread, schedule tasks

  As mentioned earlier, the executor is the ScheduledThreadPoolExecutor thread pool, and its submit() method will be called here, after it is executed. The call() method of sr will be called.

    public Object call() {
    
    
        // Being Callable saves an allocation in ThreadPoolExecutor
        run();
        return null;
    }

    @Override
    public void run() {
    
    
        lazySet(THREAD_INDEX, Thread.currentThread());
        try {
    
    
            try {
    
    
                actual.run();
            } catch (Throwable e) {
    
    
                // Exceptions.throwIfFatal(e); nowhere to go
                RxJavaPlugins.onError(e);
                throw e;
            }
        } finally {
    
    
            Object o = get(PARENT_INDEX);
            if (o != PARENT_DISPOSED && compareAndSet(PARENT_INDEX, o, DONE) && o != null) {
    
    
                ((DisposableContainer)o).delete(this);
            }

            for (;;) {
    
    
                o = get(FUTURE_INDEX);
                if (o == SYNC_DISPOSED || o == ASYNC_DISPOSED || compareAndSet(FUTURE_INDEX, o, DONE)) {
    
    
                    break;
                }
            }
            lazySet(THREAD_INDEX, null);
        }
    }

  When call() starts executing, it is already in the new thread. call() continues to call run(), you can see the lazySet(THREAD_INDEX, Thread.currentThread()) of ScheduledRunnable, and set THREAD_INDEX to the thread object it runs on.
  Then call actual.run(), where actual is the SubscribeTask object generated in subscribeActual(final Observer<? super T> observer) of the ObservableSubscribeOn class. The SubscribeTask class is the inner class of ObservableSubscribeOn, look at the run() of the SubscribeTask class.

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

  source is from ObservableSubscribeOn class, here is ObservableCreate class object. parent is the SubscribeOnObserver object. Continuing to look at subscribe() of the ObservableCreate class, it will call the subscribeActual() method:

    @Override
    protected void subscribeActual(Observer<? super T> observer) {
    
    
        CreateEmitter<T> parent = new CreateEmitter<>(observer);
        observer.onSubscribe(parent);

        try {
    
    
            source.subscribe(parent);
        } catch (Throwable ex) {
    
    
            Exceptions.throwIfFatal(ex);
            parent.onError(ex);
        }
    }

  It can be seen that the parameter SubscribeOnObserver object is encapsulated into the CreateEmitter object, and then onSubscribe(parent) of the SubscribeOnObserver object is called to notify it of being subscribed.
  Then continue to call the member source.subscribe(parent) of the ObservableCreate class object. It has also been analyzed before that source is an object that implements the ObservableOnSubscribe interface. It is the lambda expression in the parameter of Observable.create() in our example

Observable.create(emit -> {
    
                
            Thread.sleep(5000);
            emit.onNext("1");            
        })

  See, now we finally execute our time-consuming operation in a new thread. Finally, emit.onNext("1") is executed. emit is currently a CreateEmitter class object. Take a look at its onNext()

        @Override
        public void onNext(T t) {
    
    
            if (t == null) {
    
    
                onError(ExceptionHelper.createNullPointerException("onNext called with a null value."));
                return;
            }
            if (!isDisposed()) {
    
    
                observer.onNext(t);
            }
        }

  When !isDisposed(), its member variable observer.onNext(t) will be called, where observer is the SubscribeOnObserver object. Look at its onNext(t) again:

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

  The downstream here is the ObserveOnObserver object again. Look at its onNext() again

        @Override
        public void onNext(T t) {
    
    
            if (done) {
    
    
                return;
            }

            if (sourceMode != QueueDisposable.ASYNC) {
    
    
                queue.offer(t);
            }
            schedule();
        }

  ObserveOnObserver is not asynchronous unless it is set. So store the result in the queue, here, it is a single producer single consumer queue. Then call the schedule() of the ObserveOnObserver class

        void schedule() {
    
    
            if (getAndIncrement() == 0) {
    
    
                worker.schedule(this);
            }
        }
HandlerScheduler scheduling, back to the main thread

  Here it calls its member variable worker.schedule(this), but it increments itself by 1 before calling (getAndIncrement()). Its worker is the scheduler from the ObservableObserveOn class object. In the example, it is HandlerScheduler . HandlerScheduler obtains a Worker instance of the HandlerWorker type through createWorker(). Take a look at HandlerWorker's schedule()

        @Override
        @SuppressLint("NewApi") // Async will only be true when the API is available to call.
        public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
    
    
            if (run == null) throw new NullPointerException("run == null");
            if (unit == null) throw new NullPointerException("unit == null");

            if (disposed) {
    
    
                return Disposable.disposed();
            }

            run = RxJavaPlugins.onSchedule(run);

            ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);

            Message message = Message.obtain(handler, scheduled);
            message.obj = this; // Used as token for batch disposal of this worker's runnables.

            if (async) {
    
    
                message.setAsynchronous(true);
            }

            handler.sendMessageDelayed(message, unit.toMillis(delay));

            // Re-check disposed state for removing in case we were racing a call to dispose().
            if (disposed) {
    
    
                handler.removeCallbacks(scheduled);
                return Disposable.disposed();
            }

            return scheduled;
        }

  This is the Handler message passing in Android. Here the ScheduledRunnable is encapsulated, then encapsulated into an asynchronous message, sent to the main thread for execution, and finally the main thread executes the run() method of the ObserveOnObserver class. Note that thread scheduling is generated here again. Take a look at the run() of the ObserveOnObserver class:

        @Override
        public void run() {
    
    
            if (outputFused) {
    
    
                drainFused();
            } else {
    
    
                drainNormal();
            }
        }

  outputFused is true when requesting asynchronous processing, currently it is false. So drainNormal() will be executed.

        void drainNormal() {
    
    
            int missed = 1;

            final SimpleQueue<T> q = queue;
            final Observer<? super T> a = downstream;

            for (;;) {
    
    
                if (checkTerminated(done, q.isEmpty(), a)) {
    
    
                    return;
                }

                for (;;) {
    
    
                    boolean d = done;
                    T v;

                    try {
    
    
                        v = q.poll();
                    } catch (Throwable ex) {
    
    
                        Exceptions.throwIfFatal(ex);
                        disposed = true;
                        upstream.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);
                }

                missed = addAndGet(-missed);
                if (missed == 0) {
    
    
                    break;
                }
            }
        }

  We know that the result that needs to be processed now is in the queue, and now we are going to get this result, and this is what we do here.
  checkTerminated(done, q.isEmpty(), a) checks the current state of ObserveOnObserver, whether dispose() has been executed, and whether it has been completed.
  If there is no problem with the state, the result in the queue will be taken, and v = q.poll() will be called, so that the result put in the previous queue.offer(t) will be taken out. If it is taken out to be null, the queue is also considered to be empty. At this time, it also jumps out of the loop directly. Now it is not empty, so execute a.onNext(v). This a is a member variable downstream, which is actually a LambdaObserver object here, and its onNext() is called.
  If we look closely, we will find that this block is processed using two infinite loops. After the inner loop executes the onNext() of the LambdaObserver object, it will fetch the value in the queue again. If it is empty, it will call out the inner loop, and the outer loop will add -missed, the result is 0, and then jump out. In the schedule() of the ObserveOnObserver class, it added 1 to itself. So add -1 here, which is 0.
  Look again at the onNext() of the LambdaObserver object:

    @Override
    public void onNext(T t) {
    
    
        if (!isDisposed()) {
    
    
            try {
    
    
                onNext.accept(t);
            } catch (Throwable e) {
    
    
                Exceptions.throwIfFatal(e);
                get().dispose();
                onError(e);
            }
        }
    }

  Then the accept() method of LambdaObserver object member onNext will be called. onNext is a Consumer object, which is the parameter object in the subscribe() call in our example. The parameter t here is the parameter result in the onNext("1") method of the CreateEmitter object. In this way, the accept(Object o) method of the Consumer object is called in the main thread.
  After finishing this complete data transmission chain, you should now know what is going on with its thread scheduling. It implements chain calls through the design pattern of combination and encapsulation, which is mainly realized by thread pool creation thread and Android's Handler message passing mechanism.

ScheduledRunnable的setFuture(f)

  Now we have to go back to the scheduleActual() method of NewThreadWorker and continue to look at the setFuture(f) method of the ScheduledRunnable object, where f is the Future object returned after the thread pool executes.

    public void setFuture(Future<?> f) {
    
    
        for (;;) {
    
    
            Object o = get(FUTURE_INDEX);
            if (o == DONE) {
    
    
                return;
            }
            if (o == SYNC_DISPOSED) {
    
    
                f.cancel(false);
                return;
            }
            if (o == ASYNC_DISPOSED) {
    
    
                f.cancel(true);
                return;
            }
            if (compareAndSet(FUTURE_INDEX, o, f)) {
    
    
                return;
            }
        }
    }

  Here, an infinite loop is started, and the value of the ScheduledRunnable object at the position of FUTURE_INDEX will be checked. If it is DONE, it means that the execution has been completed and it will return directly. If it is SYNC_DISPOSED or ASYNC_DISPOSED, at this time, the cancel() method of the parameter Future object will be called to cancel the execution of the corresponding task, and then exit the infinite loop. If the value is different from the above, it will call compareAndSet(FUTURE_INDEX, o, f), set the FUTURE_INDEX position to the parameter f, and then exit the infinite loop.
  Basically, the logic in the example is explained here.
  The combined design method is mainly used, not only for the observed, but also for the observers inside. They are all packaged step by step, and then disassembled step by step. Among them are the thread scheduling of the thread pool and the message mechanism of the Handler.

Guess you like

Origin blog.csdn.net/q1165328963/article/details/131861999