RxJava 學習筆記(四)

1.線程控制Scheduler(二)

給 Android 開發者的 RxJava 詳解

1)Scheduler的API(二)

前面講到了,可以利用 subscribeOn()結合 observeOn()來實現線程控制,讓事件的產生和消費發生在不同的線程。可是在瞭解了 map() flatMap()等變換方法後,n能不能多切換幾次線程?

答案是:能。因爲observeOn() 指定的是 Subscriber 的線程,而這個Subscriber並不是(嚴格說應該爲『不一定是』,但這裏不妨理解爲『不是』)subscribe()參數中的 Subscriber,而是observeOn()執行時的當前 Observable所對應的 Subscriber,即它的直接下級Subscriber 。換句話說,observeOn()指定的是它之後的操作所在的線程。因此如果有多次切換線程的需求,只要在每個想要切換線程的位置調用一次observeOn()即可。上代碼:

Observable.just(1, 2, 3, 4) // IO 線程,由 subscribeOn() 指定
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.newThread())
    .map(mapOperator) // 新線程,由 observeOn() 指定
    .observeOn(Schedulers.io())
    .map(mapOperator2) // IO 線程,由 observeOn() 指定
    .observeOn(AndroidSchedulers.mainThread) 
    .subscribe(subscriber);  // Android 主線程,由 observeOn() 指定

如上,通過 observeOn()的多次調用,程序實現了線程的多次切換。

不過,不同於 observeOn()subscribeOn() 的位置放在哪裏都可以,但它是隻能調用一次的。

又有好事的(其實還是當初的我)問了:如果我非要調用多次 subscribeOn() 呢?會有什麼效果?

這個問題先放着,我們還是從 RxJava線程控制的原理說起吧。

2)Scheduler的原理(二)

下面這個是 1.1.0的 版本:

public final Observable<T> subscribeOn(Scheduler scheduler) {
    if (this instanceof ScalarSynchronousObservable) {
        return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
    }
    return nest().lift(new OperatorSubscribeOn<T>(scheduler));
}

public final Observable<T> observeOn(Scheduler scheduler) {
    if (this instanceof ScalarSynchronousObservable) {
        return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
    }
    return lift(new OperatorObserveOn<T>(scheduler));
}

可以看出其實 subscribeOn()Observable()的內部實現,也是用的lift()

下面這個 是1.1.6的版本:

public final Observable<T> subscribeOn(Scheduler scheduler) {
    if (this instanceof ScalarSynchronousObservable) {
        return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
    }
    return create(new OperatorSubscribeOn<T>(this, scheduler));
}

public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
    if (this instanceof ScalarSynchronousObservable) {
        return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
    }
    return lift(new OperatorObserveOn<T>(scheduler, delayError, bufferSize));
}

具體看圖(不同顏色的箭頭表示不同的線程):

subscribeOn()原理圖:
                                      subscribeOn

observeOn()原理圖:
                                      observeOn

從圖中可以看出,subscribeOn()observeOn()都做了線程切換的工作(圖中的 "schedule..." 部位)。不同的是, subscribeOn() 的線程切換髮生在OnSubscribe中,即在它通知上一級 OnSubscribe 時,這時事件還沒有開始發送,因此 subscribeOn()的線程控制可以從事件發出的開端就造成影響;而 observeOn() 的線程切換則發生在它內建的 Subscriber 中,即發生在它即將給下一級Subscriber 發送事件時,因此 observeOn()控制的是它後面的線程。

(1) subscribeOn()

版本爲1.1.01.1.6的 內部源碼實現不同

compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.6'
compile 'io.reactivex:rxandroid:1.2.1'
❶ 這邊是版本1.1.0的源碼

首先判斷當前observable是不是ScalarSynchronousObservable類型的,如果是則會直接執行scalarScheduleOn方法,如果不是,則會通過nest()方法創建一個ScalarSynchronousObservable並把當前的Observable包裝進去,並且lift一個OperatorSubscribeOn進去。

public final Observable<T> subscribeOn(Scheduler scheduler) {
    if (this instanceof ScalarSynchronousObservable) {
        return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
    }

    return nest().lift(new OperatorSubscribeOn<T>(scheduler));
}

subscribeOn 就是把 一個Observable轉爲 另一個 Observable, 那

一步步分析:

nest()做了什麼呢?看下面源碼可以知道,他創建了一個ScalarSynchronousObservable

public final Observable<Observable<T>> nest() {
    return just(this);
}

public final static <T> Observable<T> just(final T value) {
    return ScalarSynchronousObservable.create(value);
}

OperatorSubscribeOn做了什麼? 看他構造方法可以知道他,保存了一個scheduler對象 ,注意看他的call方法的 ,它運行時的線程,在inner這個Worker上,於是它的運行線程已經被改了

public class OperatorSubscribeOn<T> implements Operator<T, Observable<T>> {

    private final Scheduler scheduler;

    public OperatorSubscribeOn(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    @Override
    public Subscriber<? super Observable<T>> call(final Subscriber<? super T> subscriber) {
        final Worker inner = scheduler.createWorker();
        subscriber.add(inner);
        return new Subscriber<Observable<T>>(subscriber) {

            @Override
            public void onCompleted() {}

            @Override
            public void onError(Throwable e) {
                subscriber.onError(e);
            }

            @Override
            public void onNext(final Observable<T> o) {
                // inner.schedule 就是在這邊進行線程切換, 
                inner.schedule(new Action0() {

                    @Override
                    public void call() {
                        final Thread t = Thread.currentThread();

                        o.unsafeSubscribe(new Subscriber<T>(subscriber) {

                            @Override
                            public void onCompleted() {
                                subscriber.onCompleted();
                            }

                            @Override
                            public void onError(Throwable e) {
                                subscriber.onError(e);
                            }

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

                            @Override
                            public void setProducer(final Producer producer) {
                                subscriber.setProducer(new Producer() {

                                    @Override
                                    public void request(final long n) {
                                        if (Thread.currentThread() == t) {
                                            producer.request(n);
                                        } else {
                                            inner.schedule(new Action0() {
                                                @Override
                                                public void call() {
                                                    producer.request(n);
                                                }
                                            });
                                        }
                                    }

                                });
                            }

                        });
                    }
                });
            }

        };
    }
}

但是 OperatorSubscribeOn 的call 方法在什麼時候調用呢?那我們就要來看下lift(), 就是在hook.onLift(operator).call(o)這邊進行調用的,


public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
        return new Observable<R>(new OnSubscribe<R>() {
            @Override
            public void call(Subscriber<? super R> o) {
                try {
                    // 就是在這邊調用的哦!!!!切換了線程
                    Subscriber<? super T> st = hook.onLift(operator).call(o);
                    try {
                        st.onStart();
                        onSubscribe.call(st);
                    } catch (Throwable e) {
                        Exceptions.throwIfFatal(e);
                        st.onError(e);
                    }
                } catch (Throwable e) {
                    Exceptions.throwIfFatal(e);
                    o.onError(e);
                }
            }
        });
    }

❷ 這邊是版本1.1.6的源碼 建議看這個
 public final Observable<T> subscribeOn(Scheduler scheduler) {
        if (this instanceof ScalarSynchronousObservable) {
            return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
        }

        return create(new OperatorSubscribeOn<T>(this, scheduler)); // 這邊是重點,就是這邊修改了
    }

Observable.create()創建的稱之爲Observable_1,OnSubscribe_1。把subscribeOn()創建的稱之爲Observable_2OnSubscribe_2(= OperatorSubscribeOn)

那麼,前兩步就是創建了兩個的observable,和OnSubscribe,並且OnSubscribe_2中保存了Observable_1的引用,即下面代碼的source(上面代碼的this)。

public final class OperatorSubscribeOn<T> implements OnSubscribe<T> {

    final Scheduler scheduler;
    final Observable<T> source;

    public OperatorSubscribeOn(Observable<T> source, Scheduler scheduler) {
        this.scheduler = scheduler;
        this.source = source;
    }

    @Override
    public void call(final Subscriber<? super T> subscriber) {
        final Worker inner = scheduler.createWorker();
        subscriber.add(inner);

        inner.schedule(new Action0() {
            @Override
            public void call() {
                final Thread t = Thread.currentThread();

                Subscriber<T> s = new Subscriber<T>(subscriber) {
                    @Override
                    public void onNext(T t) {
                        subscriber.onNext(t);
                    }

                    @Override
                    public void onError(Throwable e) {
                        try {
                            subscriber.onError(e);
                        } finally {
                            inner.unsubscribe();
                        }
                    }

                    @Override
                    public void onCompleted() {
                        try {
                            subscriber.onCompleted();
                        } finally {
                            inner.unsubscribe();
                        }
                    }

                    @Override
                    public void setProducer(final Producer p) {
                        subscriber.setProducer(new Producer() {
                            @Override
                            public void request(final long n) {
                                if (t == Thread.currentThread()) {
                                    p.request(n);
                                } else {
                                    inner.schedule(new Action0() {
                                        @Override
                                        public void call() {
                                            p.request(n);
                                        }
                                    });
                                }
                            }
                        });
                    }
                };

                source.unsafeSubscribe(s);
            }
        });
    }
}

我們看到它實現OnSubscribe,並保存了原來的ObservableScheduler。創建一個用於在不同線程執行的Worker對象,然後worker調用schedule()去執行Observable1subscribe()

再來看subscribeOn中調用的create方法

public static <T> Observable<T> create(OnSubscribe<T> f) {
    return new Observable<T>(hook.onCreate(f));
}
//hook的onCreate方法
public <T> OnSubscribe<T> onCreate(OnSubscribe<T> f) {
    return f;
}

沒做什麼,就是把使用剛剛創建OperatorSubscribeOn作爲參數,返回給方法鏈下一個。 那到這裏我們就清晰了,其實調用了subscribeOn後,返回的Observable已經不是最開始創建那個。我們來總結一下,先把create創建的Observable稱爲Observable1,把它的OnSubscribe稱爲OnSubscribe1,把subscribeOn中創建的Observable稱爲Observable2,把它的OnSubscribe(OperatorSubscribeOn implements OnSubscribe)稱爲OnSubscribe2

 public final Subscription subscribe(Subscriber<? super T> subscriber) {
        return Observable.subscribe(subscriber, this);
    }

在方法鏈最後調用了subscribe(),應該是調用了Observable2subscribe(),那這時候就會調用到OnSubscribe2call方法,call中的參數就我們在方法鏈中傳進來的。call中會使用worker切換線程去執行,到最後纔是調用了OnSubscribe1unsafeSubscribe方法,並把重新封裝的Subscriber傳過去。而unsafeSubscribe會調用OnSubscribe1call(),所以其他流程是不變的,只是subscribe()執行的線程改了。

那爲什麼多次調用subscribeOn是無效的呢?因爲多調用一次,只是多了一層先把OnSubscribe,但其實會對Observable1subscribe()產生影響的是OnSubscribe2,其他的只會對它前面的產生影響。就好比下面這樣

new Thread() {
        @Override
        public void run() {

            new Thread() {
                @Override
                public void run() {
                    Log.i(TAG,"run");
                }
            }.start();

        }
    }.start();
(2) observeOn()

1.1.6版本源碼

public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {
    if (this instanceof ScalarSynchronousObservable) {
        return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
    }
    return lift(new OperatorObserveOn<T>(scheduler, delayError, bufferSize));
}

observeOn()這邊用到的是lift()(這邊的lift()1.1.0lift(),略有差別,但是本質call方法是一樣的),在這邊 創建了一個新的 Onsubscribe(=OnSubsribeLift)

public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
    return new Observable<R>(new OnSubscribeLift<T, R>(onSubscribe, operator));
}
public final class OnSubscribeLift<T, R> implements OnSubscribe<R> {

    static final RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook();

    final OnSubscribe<T> parent;

    final Operator<? extends R, ? super T> operator;

    public OnSubscribeLift(OnSubscribe<T> parent, Operator<? extends R, ? super T> operator) {
        this.parent = parent;
        this.operator = operator;
    }

    @Override
    public void call(Subscriber<? super R> o) {
        try {
            Subscriber<? super T> st = hook.onLift(operator).call(o);
            try {
                st.onStart();
                parent.call(st);
            } catch (Throwable e) {
                Exceptions.throwIfFatal(e);
                st.onError(e);
            }
        } catch (Throwable e) {
            Exceptions.throwIfFatal(e);
            o.onError(e);
        }
    }
}

可以看到在lift中調用了OperatorObserveOncall,傳進去Subscriber創建了一個新的Subscriber,再將其傳入onSubscribe.call()中,說明了這個新的Subscriber.onNext(),會被調用,而且它肯定在裏面還調用了原來的Subscriber.onNext()方法。 到OperatorObserveOn中就會發現,這個是新的SubscriberOperatorObserveOn的內部類ObserveOnSubscriber

public ObserveOnSubscriber(Scheduler scheduler, Subscriber<? super T> child, boolean delayError) {
    this.child = child;
    this.recursiveScheduler = scheduler.createWorker();
    .......        }

    void init() {
          ......
        Subscriber<? super T> localChild = child;
        ......
        localChild.add(recursiveScheduler);
        localChild.add(this);
    }

    @Override
    public void onStart() {
        ......
    }

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

    @Override
    public void onCompleted() {
         ......
       schedule();
    }

    @Override
    public void onError(final Throwable e) {
         ......
       schedule();
    }

    protected void schedule() {
        if (counter.getAndIncrement() == 0) {
            recursiveScheduler.schedule(this);
        }
    }

    // only execute this from schedule()
    @Override
    public void call() {
        ......

        for (;;) {
            if (checkTerminated(finished, q.isEmpty(), localChild, q)) {
                return;
            }

            ......                    
                localChild.onNext(localOn.getValue(v));
            ......           
         }
    }

    boolean checkTerminated(boolean done, boolean isEmpty, Subscriber<? super T> a, Queue<Object> q) {
        if (a.isUnsubscribed()) {
        ......
           if (e != null) {
                a.onError(e);
            } else {
                a.onCompleted();
            }
        ......            
        return false;
    }
}

我們看到onNextonCompletedonError中都調用了scheduleschedule中又調用了recursiveScheduler.schedule(this)recursiveScheduler就是根據我們設置的線程模式生成的Worker,在這裏線程就切換到我們指定的那邊了。然後又調用到this.call(),在call裏面又調用了localChild.onNextonErroronCompleted,這個localChild就是原來的Subscriber,所以onNextonErroronCompleted就會在我們指定的線程調用了。

那爲什麼會對observeOn後面的mapflatMap起作用,因爲map裏面其實也是調用了lift去創建一個新的Subscriber,所以也就會在新的線程中執行。

爲什麼每次observeOn都會起作用,因爲它跟下面這個差不多。

new Thread() {
        @Override
        public void run() {
                 map();
            new Thread() {
                @Override
                public void run() {
                    map();
                   onNext();
                }
            }.start();

        }
    }.start();
(3) 延伸:doOnSubscribe()

然而,雖然超過一個的 subscribeOn()對事件處理的流程沒有影響,但在流程之前卻是可以利用的。

在前面講Subscriber 的時候,提到過 SubscriberonStart()可以用作流程開始前的初始化。然而 onStart()由於在subscribe()發生時就被調用了,因此不能指定線程,而是隻能執行在 subscribe()被調用時的線程。這就導致如果onStart()中含有對線程有要求的代碼(例如在界面上顯示一個 ProgressBar,這必須在主線程執行),將會有線程非法的風險,因爲有時你無法預測 subscribe()將會在什麼線程執行。

而與 Subscriber.onStart()相對應的,有一個方法Observable.doOnSubscribe()。它和 Subscriber.onStart()同樣是在subscribe() 調用後而且在事件發送前執行,但區別在於它可以指定線程。默認情況下, doOnSubscribe() 執行在 subscribe() 發生的線程;而如果在doOnSubscribe()之後有 subscribeOn()的話,它將執行在離它最近的 subscribeOn()所指定的線程。

示例代碼:

Observable.create(onSubscribe)
    .subscribeOn(Schedulers.io())
    .doOnSubscribe(new Action0() {
        @Override
        public void call() {
            progressBar.setVisibility(View.VISIBLE); // 需要在主線程執行
        }
    })
    .subscribeOn(AndroidSchedulers.mainThread()) // 指定主線程
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(subscriber);

如上,在 doOnSubscribe()的後面跟一個 subscribeOn(),就能指定準備工作的線程了。

參考
給 Android 開發者的 RxJava 詳解
RxJava中的線程切換源碼分析

發佈了34 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章