圖解RxJava(二)

概述

在 RxJava 中可以通過 subscribeOn/observeOn 很方便地完成上下游指定線程的切換,日常開發除了一些常用的Rx 操作符外,這兩個方法也是打交道最多的。最初學習 RxJava 的時候總是死記硬背:subscribeOn 用于指定上游線程,observeOn 用于指定下游線程,多次用 subscribeOn 指定上游線程只有第一次有效,多次用 observeOn 指定下次線程,每次都有效…很久不用之后,總是把這兩個方法搞混,那么這兩個方法內部是怎么實現的呢?本篇先分析subscribeOn 方法。

例子

先回顧上篇文章的流程,飯店(Observable)開張前提要有廚師(ObservableOnSubscribe),接著改名叫沙縣小吃(ObservableCreate),飯店接客(Observable.subscribe(observer)),創建服務員(CreateEmitter)把顧客和廚師關聯起來,之后廚師每做一道菜都通過服務員端給顧客,整個流程如下:

](http://upload-images.jianshu.io/upload_images/7125608-e0656a14ab601797.gif?imageMogr2/auto-orient/strip)](http://7xjvg5.com1.z0.glb.clouddn.com/rxjava2_01.gif)

我們都知道 Andriod 有主線程,在未指定線程切換操作的情況下,上圖的流程是跑在主線程中,另外主線程中往往還存在其他任務需要執行,所以結合線程來看應該是這樣的

上圖給人一種感覺,好像廚師的菜是「秒做」出來的,然而我們都知道現實生活中廚師做菜是需要時間的,在安卓中,主線程執行耗時操作會阻塞后續的任務,還有可能引起 ANR,所以廚師做菜的操作不能放在主線程中 。下面讓上游睡5秒模擬耗時操作

上游:

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 744px;">

final Observable<String> source = Observable.create(new ObservableOnSubscribe<String>() {

        @Override

        public void subscribe(ObservableEmitter<String> e) throws Exception {

            Thread.sleep(5000);

            Log.e(TAG, "服務員從廚師那取得 扁食" + " 線程名: "+Thread.currentThread().getName());

            e.onNext("扁食");

            Log.e(TAG, "服務員從廚師那取得 拌面" + " 線程名: " + Thread.currentThread().getName());

            e.onNext("拌面");

            Log.e(TAG, "服務員從廚師那取得 蒸餃" + " 線程名: " + Thread.currentThread().getName());

            e.onNext("蒸餃");

            Log.e(TAG, "廚師告知服務員菜上好了" + " 線程名: " + Thread.currentThread().getName());

            e.onComplete();

        }

    });

</pre>

|

</figure>

下游:

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 737px;">

Observer<String> observer = new Observer<String>() {

        @Override

        public void onSubscribe(Disposable d) {

            Log.d(TAG, "來個沙縣套餐!!!" + " 線程名: " + Thread.currentThread().getName());

        }

        @Override

        public void onNext(String s) {

            Log.d(TAG, "服務員端給顧客  " + s + " 線程名: " + Thread.currentThread().getName());

        }

        @Override

        public void onError(Throwable e) {

        }

        @Override

        public void onComplete() {

            Log.d(TAG, "服務員告訴顧客菜上好了" + " 線程名: " + Thread.currentThread().getName());

        }

    };

</pre>

|

</figure>

建立聯系,以及執行其他任務(這里只是打了個 log )

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 213px;">

source.subscribe(observer);

Log.d(TAG, "其他任務執行");

</pre>

|

</figure>

打印如下:

可以看到,由于上游耗時,導致主線程中「其他任務」被阻塞了,因此需要新建一個子線程來處理上游的耗時任務,使用 RxJava 的 subscribeOn 就能輕松實現,修改代碼:

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 321px;">

source.subscribeOn(Schedulers.newThread())

            .subscribe(observer);

Log.e(TAG, "其他任務執行");

</pre>

|

</figure>

打印如下:

此時「其他任務」不會被阻塞。從上面的 log 可以看到,創建了 RxNewThreadScheduler-1 的子線程來執行上游的耗時任務,并且此時下游除 onSubscribe 外,所有方法都執行在子線程中,它是怎么做到的?(通常情況下游會調用 observeOn(AndroidSchedulers.mainThread()) 來更新UI,下篇分析)。

源碼分析

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 321px;">

source.subscribeOn(Schedulers.newThread())

            .subscribe(observer);

</pre>

|

</figure>

上面的代碼簡短優雅,其實做了很多事情。基于上篇的分析,在執行完 Observable.create 和 new Observer 后此時主線程應該是下面的樣子

Schedulers.newThread()

Scheduler 翻譯為調度器,RxJava2 中 Scheduler 的一些常用子類如下:

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 478px;">

static final class SingleHolder {

static final Scheduler DEFAULT = new SingleScheduler();

}

static final class ComputationHolder {

static final Scheduler DEFAULT = new ComputationScheduler();

}

static final class IoHolder {

static final Scheduler DEFAULT = new IoScheduler();

}

static final class NewThreadHolder {

static final Scheduler DEFAULT = new NewThreadScheduler();

}

</pre>

|

</figure>

Schedulers.newThread() 會初始化 NewThreadScheduler ;

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 692px;">

public final class NewThreadScheduler extends Scheduler {

final ThreadFactory threadFactory;

//看著很眼熟,原來我們上游的線程名稱的一部分就是這么起的"RxNewThreadScheduler-1"

private static final String THREAD_NAME_PREFIX = "RxNewThreadScheduler";

//線程工廠

private static final RxThreadFactory THREAD_FACTORY;

/** The name of the system property for setting the thread priority for this Scheduler. */

//用來設置線程優先級的key

private static final String KEY_NEWTHREAD_PRIORITY = "rx2.newthread-priority";

//靜態代碼塊

static {

    //確定線程的優先級,這里初始化為5 NORM_PRIORITY

    int priority = Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY,

            Integer.getInteger(KEY_NEWTHREAD_PRIORITY, Thread.NORM_PRIORITY)));

    //初始化線程工廠,傳入線程名稱和優先級

    THREAD_FACTORY = new RxThreadFactory(THREAD_NAME_PREFIX, priority);

}

//賦值

public NewThreadScheduler() {

    this(THREAD_FACTORY);

}

//賦值

public NewThreadScheduler(ThreadFactory threadFactory) {

    this.threadFactory = threadFactory;

}

@NonNull

@Override

//這個方法很重要,很重要,很重要!!!后面會用到

public Worker createWorker() {

    return new NewThreadWorker(threadFactory);

}

}

</pre>

|

</figure>

上面的注釋已經解釋得很清楚了,在初始化 NewThreadScheduler 的時候會創建 RxThreadFactory,并指明了該線程工廠之后生產線程的名稱和默認優先級;RxThreadFactory 是 ThreadFactory 的子類,也沒多少代碼

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 707px;">

public RxThreadFactory(String prefix, int priority) {

this(prefix, priority, false);

}

public RxThreadFactory(String prefix, int priority, boolean nonBlocking) {

this.prefix = prefix;

this.priority = priority;

this.nonBlocking = nonBlocking;

}

@Override

//生產新線程的方法!!!

public Thread newThread(Runnable r) {

StringBuilder nameBuilder = new StringBuilder(prefix).append('-').append(incrementAndGet());

String name = nameBuilder.toString();

Thread t = nonBlocking ? new RxCustomThread(r, name) : new Thread(r, name);

t.setPriority(priority);

t.setDaemon(true);

return t;

}

</pre>

|

</figure>

RxThreadFactory 中的 newThread 方法用來生產新線程。Schedulers.newThread() 到此就完成了它的工作,總結下來就是:

1.創建線程調度器 NewThreadScheduler;

2.創建線程工廠 RxThreadFactory ;

到目前為止這些操作都是在主線程中執行的,子線程還未被創建。

subscribeOn(Scheduler scheduler)

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 614px;">

public final Observable<T> subscribeOn(Scheduler scheduler) {

ObjectHelper.requireNonNull(scheduler, "scheduler is null");

return RxJavaPlugins.onAssembly(new ObservableSubscribeOn<T>(this, scheduler));

}

</pre>

|

</figure>

該方法返回 Observable ,創建了 ObservableSubscribeOn ,名字起得又很容易讓人頭暈…這里就不畫關系圖了,只關心它的屬性即可,它是 Observable(飯店) 的子類,結合我們舉的例子,就給它起名黃燜雞飯店;this 就是上面傳過來的沙縣小吃(ObservableCreate) ;初始化如下:

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 664px;">

public final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T, T> {

final Scheduler scheduler;

public ObservableSubscribeOn(ObservableSource<T> source, Scheduler scheduler) {

    super(source);

    this.scheduler = scheduler;

}

//省略其他代碼

}

</pre>

|

</figure>

目前為止這些操作都是在主線程中執行,子線程還未創建

subscribe(Observer observer)

通過上篇學習可知,subscribe(observer) 內部會調用 subscribeActual(observer) ,該方法是個抽象方法,具體實現在 Observable(飯店) 的子類,現在是 ObservableSubscribeOn(黃燜雞飯店)。

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 578px;">

public void subscribeActual(final Observer<? super T> s) {

//注釋1

final SubscribeOnObserver<T> parent = new SubscribeOnObserver<T>(s);

//注釋2

s.onSubscribe(parent);

//注釋3

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

}

</pre>

|

</figure>

注釋1 又冒出來一個 SubscribeOnObserver,同樣只關心它的屬性,SubscribeOnObserver 是AtomicReference的子類(保證原子性),同時實現了 Observer(也是個顧客) 和 Disposable(保證一次性操作) 接口;為了方便理解,假設之前傳的顧客叫小明,這里的顧客叫小紅,小紅會持有小明的引用(actual),之后一系列的方法實際上會調用到小明的方法。

注釋2 執行顧客小明的 onSubscribe 方法,我們發現到目前為止還沒有創建過子線程,所以解釋了上面 log 下游 onSubscribe 打印線程名為 main。

注釋3 分為下面3步

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 549px;">

           步驟③                    步驟②             步驟①

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

</pre>

|

</figure>

步驟① SubscribeTask 是 ObservableSubscribeOn(黃燜雞飯店) 的內部類,實現了 Runnable 接口

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 454px;">

final class SubscribeTask implements Runnable {

private final SubscribeOnObserver<T> parent;

SubscribeTask(SubscribeOnObserver<T> parent) {

    this.parent = parent;

}

@Override

public void run() {

    //這里的 source 就是之前傳的 ObservableCreate(沙縣小吃)

    source.subscribe(parent);

}

}

</pre>

|

</figure>

如果 run 方法被觸發,那么執行順序是:

Observable.subscribe() —> Observable.subscribeActual() —> ObservableCreate.subscribeActual(),繞了一圈又回到上篇的那個流程。為了方便理解,SubscribeTask 就是黃燜雞飯店(ObservableSubscribeOn)的「任務」也就是沙縣小吃的「做菜」(ObservableCreate.subscribeActual)。所以現在萬事具備,只差子線程了。

步驟② Scheduler.scheduleDirect()

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 578px;">

/**

  • Schedules the given task on this scheduler non-delayed execution.

  • <p>

  • This method is safe to be called from multiple threads but there are no

  • ordering guarantees between tasks.

  • @param run the task to execute

  • @return the Disposable instance that let's one cancel this particular task.

  • @since 2.0

*/

//這里調度的時候不保證順序

//第二個參數為0,不延時,直接調度

@NonNull

public Disposable scheduleDirect(@NonNull Runnable run) {

return scheduleDirect(run, 0L, TimeUnit.NANOSECONDS);

}

</pre>

|

</figure>

注意這個方法的注釋,該方法調度的時候不保證順序,所以平時在配合使用 subscribeOn(子線程)/observeOn(主線程) 會出現上下游輸出順序不確定的情況(比如有時候上游生產了3個后才逐個發送給下游,有時上游生產了2個,就開始發送給下游),這也是多線程的一個特點。當然這里不會出現這個情況,因為從輸出來看,此時上下游都在一個子線程里。貌似跑遠了…繼續分析

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 685px;">

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;

}

// Scheduler 中為抽象方法

public abstract Worker createWorker();

</pre>

|

</figure>

前面創建 NewThreadScheduler 的時候說 createWorker() 方法很重要,這里派上用場了:

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 642px;">

//NewThreadScheduler.java

public Worker createWorker() {

return new NewThreadWorker(threadFactory);

}

//NewThreadWorker.java

public NewThreadWorker(ThreadFactory threadFactory) {

    //實例化 ScheduledExecutorService 對象 executor 管理線程池

    executor = SchedulerPoolFactory.create(threadFactory);

}

//SchedulerPoolFactory.java

public static ScheduledExecutorService create(ThreadFactory factory) {

//默認線程池大小為1

final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);

if (exec instanceof ScheduledThreadPoolExecutor) {

    ScheduledThreadPoolExecutor e = (ScheduledThreadPoolExecutor) exec;

    POOLS.put(e, exec);

}

return exec;

}

</pre>

|

</figure>

NewThreadWorker 內部維護一個線程池 ScheduledExecutorService , 主要作用是提供延時調度和周期性調度,默認線程池大小為1,線程池里的線程通過我們傳的線程工廠創建。

之后把 NewThreadWorker 和步驟①中的任務包裝成 DisposeTask,又是一個Runnable

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 278px;">

public void run() {

//注意這里會獲取當前所在線程

runner = Thread.currentThread();

try {

    //在當前線程中執行

    decoratedRun.run();

} finally {

    //執行完后斷開

    dispose();

    runner = null;

}

}

</pre>

|

</figure>

最后會執行 NewThreadWorker.schedule 方法

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 735px;">

public Disposable schedule(@NonNull final Runnable action, long delayTime, @NonNull TimeUnit unit) {

if (disposed) {

    return EmptyDisposable.INSTANCE;

}

return scheduleActual(action, delayTime, unit, null);

}

public Disposable scheduleDirect(final Runnable run, long delayTime, TimeUnit unit) {

ScheduledDirectTask task = new ScheduledDirectTask(RxJavaPlugins.onSchedule(run));

try {

    Future<?> f;

    //不延時,直接調度

    if (delayTime <= 0L) {

        //此時任務執行在子線程中

        f = executor.submit(task);

    } else {

        f = executor.schedule(task, delayTime, unit);

    }

    task.setFuture(f);

    return task;

} catch (RejectedExecutionException ex) {

    RxJavaPlugins.onError(ex);

    return EmptyDisposable.INSTANCE;

}

}

</pre>

|

</figure>

到這里終于看到任務(ObservableCreate.subscribeActual)執行在子線程中。

步驟③ parent.setDisposable 設置可中斷。至此流程如下

之后所有的事情都是在子線程中進行的,上篇已經分析過了

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 598px;">

protected void subscribeActual(Observer<? super T> observer) {

//創建服務員,并和顧客聯系,這里的顧客是小紅

CreateEmitter<T> parent = new CreateEmitter<T>(observer);

//執行顧客小紅的的 onSubscribe ,注意這里不會再回調顧客小明的onSubscribe

//因為顧客小紅的 onSubscribe 中只是將接收事件的行為設置成一次性,并沒有回調小明方法

observer.onSubscribe(parent);

try {

    //廚師做菜,并和服務員聯系

    source.subscribe(parent);

} catch (Throwable ex) {

    Exceptions.throwIfFatal(ex);

    parent.onError(ex);

}

}

</pre>

|

</figure>

后續還有:服務員端菜(CreateEmitter.onNext) —> 顧客小紅拿到菜(SubscribeOnObserver.onNext) —> 顧客小明拿到菜(Observer.onNext),模擬如下:

多次subscribeOn

<figure class="highlight plain" style="display: block; margin: 20px 0px; overflow: auto; padding: 0px; font-size: 13px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border-radius: 1px; font-family: Lato, "PingFang SC", "Microsoft YaHei", sans-serif; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(134, 145, 148); background: rgb(239, 242, 243); line-height: 1.6; border: none; text-align: right;">

1

2

3

</pre>

|

<pre style="overflow: auto; font-family: consolas, Menlo, "PingFang SC", "Microsoft YaHei", monospace; font-size: 13px; margin: 0px; padding: 10px; color: rgb(77, 77, 76); background: rgb(247, 247, 247); line-height: 1.6; border: none; width: 335px;">

source.subscribeOn(Schedulers.newThread())

    .subscribeOn(Schedulers.newThread())

    .subscribe(observer);

</pre>

|

</figure>

上面我先把任務從一個線程切換到另一個線程,但是只有最先指定的有效(可以用 io 線程更容易看出差別),這是為啥呢?通過上面的分析我們知道,subscribeOn() 每次會返回一個 Observable ,為了方便理解,把先指定返回的Observable 叫黃燜雞1號店,后指定返回的 Observable 叫黃燜雞2號店,第一個 subscribeOn() 執行:

黃燜雞1號店創建的時候會持有沙縣小吃的引用,接著第二個 subscribeOn() 執行:

黃燜雞2號店創建的時候會持有黃燜雞1號店的引用,接著執行 subscribe(observer) 方法,會先調用黃燜雞2號店的 subscribeActual() 方法:

接著調用黃燜雞2號店的 subscribeActual() 方法 :

可以看到此時黃燜雞1號店的 Worker 和小紅是創建在子線程2的,并在子線程2中把當前線程切到了新的線程,后面的操作就和上面一樣了,這就是為啥多次通過 subscribeOn 指定線程,只有最先指定的有效。

最后

多次用 subscribeOn 指定上游線程真的只有第一次有效嗎?其實不然,具體可以看Dávid Karnok 的這篇博客,其中涉及到一些 Rx 操作符操作,本篇只是介紹 subscribeOn 的使用和原理,就不引入其他內容,mark 下日后再撿起來看。

感謝

When multiple subscribeOn()s do have effect

SubscribeOn and ObserveOn

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容