星球話題:用過RxJava和RxAndroid嗎?RxAndroid切換線程是怎么實現(xiàn)的呢?
去年知乎上參加了玉剛的Live贸伐,聽大神講解職業(yè)規(guī)劃。隨后入了微信群怔揩,去年11月份也加入主席的星球捉邢。由于去年十一月份剛好接了外包工作,比較忙就忽略星球的任務(wù)商膊,說來慚愧伏伐,到現(xiàn)在還沒有交過一次作業(yè)。再加上今年年初想換工作翘狱,就忙于復(fù)習(xí)秘案,星球的作業(yè)就落下,希望從今天開始潦匈,把作業(yè)補回來阱高。
年初也去試水,發(fā)覺現(xiàn)在android的要求真的是高茬缩〕嗑可能也是自己比較菜吧,試了三家沒有拿到offer凰锡。今年計劃未舟,好好復(fù)習(xí)安卓知識圈暗,學(xué)點RN、小程序裕膀、PWA员串,爭取拿到好的offer。女朋友說我昼扛,晚上想了千萬條路寸齐,隔天起來走原路。哎抄谐,反正還是得腳踏實地渺鹦,一步一步學(xué)習(xí)。說干就干蛹含,下邊我們開始學(xué)習(xí)毅厚。
(1)RxJava 基本概念:
1、Observable (可觀察者浦箱,即被觀察者)
2吸耿、Observer (觀察者)
3、subscribe (訂閱)憎茂、事件
4珍语、Scheduler 調(diào)度器,相當(dāng)于線程控制器
Rxjava 實現(xiàn):
1竖幔、創(chuàng)建Observable(被觀察者):
mObservable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("2018年");
subscriber.onNext("CBA");
subscriber.onNext("遼寧隊");
subscriber.onNext("奪冠");
subscriber.onCompleted();
}
});
這里傳入了一個OnSubscribe對象作為參數(shù)板乙。OnSubscribe會被存儲在返回的 Observable對 象中,它的作用相當(dāng)于一個計劃表拳氢,當(dāng)Observable被訂閱的時候募逞,OnSubscribe的call()方法會自動被調(diào)用,事件序列就會依照設(shè)定依次觸發(fā)(對于上面的代碼馋评,就是觀察者Subscriber 將會被調(diào)用四次 onNext() 和一次 onCompleted())放接。這樣,由被觀察者調(diào)用了觀察者的回調(diào)方法留特,就實現(xiàn)了由被觀察者向觀察者的事件傳遞纠脾。
2、創(chuàng)建Observer(觀察者):
mObserver = new Observer<String>() {
@Override
public void onCompleted() {
LogUtil.d("onCompleted:");
}
@Override
public void onError(Throwable e) {
LogUtil.d("onError:"+e);
}
@Override
public void onNext(String s) {
LogUtil.d("onNext:"+s);
}
};
Subscriber是實現(xiàn)Observer的抽象類蜕青,用法也一樣:
mSubscriber = new Subscriber() {
@Override
public void onCompleted() {
LogUtil.d("onCompleted:");
}
@Override
public void onError(Throwable e) {
LogUtil.d("onError:"+e);
}
@Override
public void onNext(Object o) {
LogUtil.d("onNext:"+o);
}
};
Subscirber與Observe 的區(qū)別是:
1苟蹈、onStart(): 這是 Subscriber 增加的方法。它會在 subscribe 剛開始右核,而事件還未發(fā)送之 前被調(diào)用慧脱,可以用于做一些準備工作。
2贺喝、unsubscribe(): 這是 Subscriber 所實現(xiàn)的另一個接口 Subscription 的方法菱鸥,用于取消訂閱宗兼。
3、subscribe()訂閱:
mObservable.subscribe(mObserver);
或者
mObservable.subscribe(mSubscriber);
以上1氮采、2殷绍、3過程也可以寫成:
Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("2018年");
subscriber.onNext("CBA");
subscriber.onNext("遼寧隊");
subscriber.onNext("奪冠");
subscriber.onCompleted();
}
}).subscribe(new Observer<String>() {
@Override
public void onCompleted() {
LogUtil.d("onCompleted:");
}
@Override
public void onError(Throwable e) {
LogUtil.d("onError:"+e);
}
@Override
public void onNext(String s) {
LogUtil.d("onNext:"+s);
}
});
結(jié)果:
(2)Rxjava常見操作符
下邊我們來了解一下Rxjava 常見操作符:
just:將傳入的參數(shù)依次發(fā)送出來
Observable observable = Observable.just("2018年", "CBA", "遼寧隊","奪冠");
// 將會依次調(diào)用:
// onNext("2018年");
// onNext("CBA");
// onNext("遼寧隊");
// onNext("奪冠");
// onCompleted();
from(T[]) / from(Iterable<? extends T>) : 將傳入的數(shù)組或 Iterable 拆分成具體對象后,依次發(fā)送出:
String[] words = {"2018年", "CBA", "遼寧隊","奪冠"};
Observable observable = Observable.from(words);
// 將會依次調(diào)用:
// onNext("2018年");
// onNext("CBA");
// onNext("遼寧隊");
// onNext("奪冠");
// onCompleted();
(3)Rxjava如何切換線程:Scheduler
我們回到文章開頭鹊漠,RxAndroid切換線程是怎么實現(xiàn)的呢篡帕?看下邊一個例子:
Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
LogUtil.d("subscriber:");
subscriber.onNext("2018年");
subscriber.onNext("CBA");
subscriber.onNext("遼寧隊");
subscriber.onNext("奪冠");
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
LogUtil.d("onCompleted:");
}
@Override
public void onError(Throwable e) {
LogUtil.d("onError:"+e);
}
@Override
public void onNext(String s) {
LogUtil.d("onNext:"+s);
}
});
subscribeOn(): 指定subscribe()所發(fā)生的線程,即 Observable.OnSubscribe被激活時所處的線程贸呢。或者叫做事件產(chǎn)生的線程拢军。
observeOn(): 指定 Subscriber 所運行在的線程楞陷。或者叫做事件消費的線程茉唉。
我們來了解一下Scheduler:
在不指定線程的情況下固蛾,RxJava遵循的是線程不變的原則,即:在哪個線程調(diào)用 subscribe(),就在哪個線程生產(chǎn)事件度陆;在哪個線程生產(chǎn)事件艾凯,就在哪個線程消費事件。如果需要切換線程懂傀,就需要用到Scheduler(調(diào)度器),下面是Scheduler的API:
1趾诗、Schedulers.immediate(): 直接在當(dāng)前線程運行,相當(dāng)于不指定線程蹬蚁。這是默認的 Scheduler恃泪。
2、Schedulers.newThread(): 總是啟用新線程犀斋,并在新線程執(zhí)行操作贝乎。
Schedulers.io(): I/O 操作(讀寫文件、讀寫數(shù)據(jù)庫叽粹、網(wǎng)絡(luò)信息交互等)所使用的 Scheduler览效。行為模式和 newThread() 差不多,區(qū)別在于 io() 的內(nèi)部實現(xiàn)是是用一個無數(shù)量上限的線程池虫几,可以重用空閑的線程锤灿,因此多數(shù)情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中持钉,可以避免創(chuàng)建不必要的線程衡招。
3、Schedulers.computation(): 計算所使用的 Scheduler每强。這個計算指的是 CPU 密集型計算始腾,即不會被 I/O 等操作限制性能的操作州刽,例如圖形的計算。這個 Scheduler 使用的固定的線程池浪箭,大小為 CPU 核數(shù)穗椅。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU奶栖。
4匹表、Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行宣鄙。
有了這幾個 Scheduler 袍镀,就可以使用 subscribeOn() 和 observeOn() 兩個方法來對線程進行控制了。
那么我們有個疑問冻晤,Rxjava內(nèi)部是如何切換線程?首先我們來看subscribeOn()
subscribeOn()源碼:
public final Observable<T> subscribeOn(Scheduler scheduler) {
if (this instanceof ScalarSynchronousObservable) {
return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler);
}
return create(new OperatorSubscribeOn<T>(this, scheduler));
}
我們看到參數(shù)是傳入Scheduler 調(diào)度器苇羡,然后創(chuàng)建了新的Observable,我們看到OperatorSubscribeOn這個對象鼻弧,OperatorSubscribeOn原始Observable對象和調(diào)度器scheduler设江,那么這個OperatorSubscribeOn是什么呢,我們看下源碼:
public final class OperatorSubscribeOn<T> implements OnSubscribe<T> {
final Scheduler scheduler; //調(diào)度器
final Observable<T> source; //原始Observable
public OperatorSubscribeOn(Observable<T> source, Scheduler scheduler) {
this.scheduler = scheduler;
this.source = source;
}
//①.原始觀察者訂閱了新的Observable后攘轩,將執(zhí)行此call方法
@Override
public void call(final Subscriber<? super T> subscriber) {
final Worker inner = scheduler.createWorker();
subscriber.add(inner);
//②. call方法中使用傳入的調(diào)度器創(chuàng)建的Worker對象的schedule方法切換線程
inner.schedule(new Action0() {
@Override
public void call() {
final Thread t = Thread.currentThread();
//③ .創(chuàng)建了一個新的觀察者
Subscriber<T> s = new Subscriber<T>(subscriber) {
@Override
public void onNext(T t) {
//⑤. 新的觀察者收到數(shù)據(jù)后直接發(fā)送給原始觀察者
subscriber.onNext(t);
}
@Override
public void onError(Throwable e) {
try {
//⑤. 新的觀察者收到數(shù)據(jù)后直接發(fā)送給原始觀察者
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);
}
});
}
}
});
}
};
//④. 在切換的線程中叉存,新的觀察者訂閱原始Observable,用來接收數(shù)據(jù)
source.unsafeSubscribe(s);
}
});
}
}
OperatorSubscribeOn是實現(xiàn)Observable的OnSubscribe 接口
public interface OnSubscribe<T> extends Action1<Subscriber<? super T>> {
// cover for generics insanity
}
public interface Action1<T> extends Action {
void call(T t);
}
上面源碼中注釋已經(jīng)寫的很清楚了度帮,OperatorSubscribeOn字意上來理解歼捏,Operator操作員,相當(dāng)于媒介笨篷,為新的Observable發(fā)射數(shù)據(jù)甫菠。它創(chuàng)建了一個新的觀察者訂閱原始Observable,這樣就可以接受原始Observable發(fā)射的數(shù)據(jù)冕屯,然后直接發(fā)送給原始觀察者寂诱。
所以O(shè)peratorSubscribeOn也是間接實現(xiàn)了Action1,我們來看OperatorSubscribeOn在call()方法里邊操作了什么安聘。在call方法中通過scheduler.createWorker().schedule()完成線程的切換痰洒,這里就牽扯到兩個對象了,Scheduler和Worker浴韭。Scheduler是個抽象類丘喻,是從外邊傳進來的。我們就看一個簡單的Schedulers.newThread()念颈,其他也是從類似泉粉,下面一步一步看源碼:
/**
* Static factory methods for creating Schedulers.
*/
public final class Schedulers {
//各種調(diào)度器對象
private final Scheduler computationScheduler;
private final Scheduler ioScheduler;
private final Scheduler newThreadScheduler;
private static final AtomicReference<Schedulers> INSTANCE = new AtomicReference<Schedulers>();
......
//構(gòu)造方法
private Schedulers() {
RxJavaSchedulersHook hook = RxJavaPlugins.getInstance().getSchedulersHook();
......
Scheduler nt = hook.getNewThreadScheduler();
if (nt != null) {
newThreadScheduler = nt;
} else {
//①.創(chuàng)建newThreadScheduler對象
newThreadScheduler = RxJavaSchedulersHook.createNewThreadScheduler();
}
}
}
//②. 獲取NewThreadScheduler對象
public static Scheduler newThread() {
return getInstance().newThreadScheduler;
}
Schedulers中保存了幾個調(diào)度器對象,在Schedulers被加載的時候,他們就被初始化了嗡靡,Schedulers就像是一個調(diào)度器的控制器跺撼,跟蹤newThreadScheduler,看到newThreadScheduler在RxJavaSchedulersHook.createNewScheduler()實例化讨彼。CTRL+鼠標左鍵跟createNewScheduler()方法進去歉井,最終調(diào)到NewThreadScheduler(ThreadFactory threadFactory)的方法:
**
* Schedules work on a new thread.
*/
public final class NewThreadScheduler extends Scheduler {
private final ThreadFactory threadFactory;
public NewThreadScheduler(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
}
@Override
public Worker createWorker() {
return new NewThreadWorker(threadFactory);
}
}
NewThreadScheduler就是我們調(diào)用subscribeOn(Schedulers.newThread() )傳入的調(diào)度器對象,每個調(diào)度器對象都有一個createWorker方法用于創(chuàng)建一個Worker對象哈误,而NewThreadScheduler對應(yīng)創(chuàng)建的Worker是一個叫NewThreadWorker的對象哩至,在新產(chǎn)生的OperatorSubscribeOn計劃表中就是通過NewThreadWorker.schedule(Action0)實現(xiàn)線程的切換,下面我們跟蹤schedule(Action0)方法:
public class NewThreadWorker extends Scheduler.Worker implements Subscription {
private final ScheduledExecutorService executor; //
public NewThreadWorker(ThreadFactory threadFactory) {
//創(chuàng)建一個線程池
ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, threadFactory);
executor = exec;
}
@Override
public Subscription schedule(final Action0 action) {
return schedule(action, 0, null);
}
@Override
public Subscription schedule(final Action0 action, long delayTime, TimeUnit unit) {
return scheduleActual(action, delayTime, unit);
}
//重要:worker.schedule()最終調(diào)用的是這個方法
public ScheduledAction scheduleActual(final Action0 action, long delayTime, TimeUnit unit) {
//return action;
Action0 decoratedAction = schedulersHook.onSchedule(action);
//ScheduledAction就是一個Runnable對象蜜自,在run()方法中調(diào)用了Action0.call()
ScheduledAction run = new ScheduledAction(decoratedAction);
Future<?> f;
if (delayTime <= 0) {
f = executor.submit(run); //將Runnable對象放入線程池中
} else {
f = executor.schedule(run, delayTime, unit); //延遲執(zhí)行
}
run.add(f);
return run;
}
...
}
我們發(fā)現(xiàn)OperatorSubscribeOn計劃表中通過NewThreadWorker.schedule(Action0)菩貌,將Action0放入到一個線程池中執(zhí)行,這樣就實現(xiàn)了線程的切換重荠。
多次subscribeOn()的情況:
我們發(fā)現(xiàn)菜谣,每次使用subscribeOn都會產(chǎn)生一個新的Observable,并產(chǎn)生一個新的計劃表OnSubscribe晚缩,目標Subscriber最后訂閱的將是最后一次subscribeOn產(chǎn)生的新的Observable。在每個新的OnSubscribe的call方法中都會有一個產(chǎn)生一個新的線程媳危,在這個新線程中訂閱上一級Observable荞彼,并創(chuàng)建一個新的Subscriber接受數(shù)據(jù),最終原始Observable將在第一個新線程中發(fā)射數(shù)據(jù)待笑,然后傳送給給下一個新的觀察者鸣皂,直到傳送到目標觀察者,所以多次調(diào)用subscribeOn只有第一個起作用(這只是表面現(xiàn)象暮蹂,其實每個subscribeOn都切換了線程寞缝,只是最終目標Observable是在第一個subscribeOn產(chǎn)生的線程中發(fā)射數(shù)據(jù)的)。
?多次subscribeOn()只有第一個會起作用仰泻,后面的只是在第一個的基礎(chǔ)上在外面套了一層殼荆陆,就像下面的偽代碼,最后執(zhí)行是在第一個新線程中執(zhí)行:
...
//第3個subscribeOn產(chǎn)生的新線程
new Thread(){
@Override
public void run() {
Subscriber s1 = new Subscriber();
//第2個subscribeOn產(chǎn)生的新線程
new Thread(){
@Override
public void run() {
Subscriber s2 = new Subscriber();
//第1個subscribeOn產(chǎn)生的新線程
new Thread(){
@Override
public void run() {
Subscriber<T> s3 = new Subscriber<T>(subscriber) {
@Override
public void onNext(T t) {
subscriber.onNext(t);
}
...
};
//①. 最后一個新觀察者訂閱原始Observable
原始Observable.subscribe(s3);
//②. 原始Observable將在此線程中發(fā)射數(shù)據(jù)
//③. 最后一個新的觀察者s3接受數(shù)據(jù)
//④. s3收到數(shù)據(jù)后集侯,直接發(fā)送給s2被啼,s2收到數(shù)據(jù)后傳給s1,...最后目標觀察者收到數(shù)據(jù)
}
}.start();
}
}.start();
}
}.start();
observeOn原理:
observeOn調(diào)用的是lift操作符,lift操作符棠枉。lift有點難理解浓体,簡單點說就是在 Observable 執(zhí)行了 lift(Operator) 方法之后,會返回一個新的 Observable辈讶,這個新的 Observable 會像一個代理一樣命浴,負責(zé)接收原始的 Observable 發(fā)出的事件,并在處理后發(fā)送給 Subscriber。
observeOn一樣創(chuàng)建了一個代理的Observable生闲,并創(chuàng)建一個代理觀察者接受上一級Observable的數(shù)據(jù)媳溺,代理觀察者收到數(shù)據(jù)之后會開啟一個線程,在新的線程中跪腹,調(diào)用下一級觀察者的onNext褂删、onCompete、onError方法冲茸。
我們看看observeOn操作符的源碼:
public final class OperatorObserveOn<T> implements Observable.Operator<T, T> {
private final Scheduler scheduler;
//創(chuàng)建代理觀察者屯阀,用于接收上一級Observable發(fā)射的數(shù)據(jù)
@Override
public Subscriber<? super T> call(Subscriber<? super T> child) {
if (scheduler instanceof ImmediateScheduler) {
return child;
} else if (scheduler instanceof TrampolineScheduler) {
return child;
} else {
ObserveOnSubscriber<T> parent = new ObserveOnSubscriber<T>(scheduler, child, delayError, bufferSize);
parent.init();
return parent;
}
}
//代理觀察者
private static final class ObserveOnSubscriber<T> extends Subscriber<T> implements Action0 {
final Subscriber<? super T> child;
final Scheduler.Worker recursiveScheduler;
final NotificationLite<T> on;
final Queue<Object> queue;
//接受上一級Observable發(fā)射的數(shù)據(jù)
@Override
public void onNext(final T t) {
if (isUnsubscribed() || finished) {
return;
}
if (!queue.offer(on.next(t))) {
onError(new MissingBackpressureException());
return;
}
schedule();
}
@Override
public void onCompleted() {
...
schedule();
}
@Override
public void onError(final Throwable e) {
...
schedule();
}
//開啟新線程處理數(shù)據(jù)
protected void schedule() {
if (counter.getAndIncrement() == 0) {
recursiveScheduler.schedule(this);
}
}
// only execute this from schedule()
//在新線程中將數(shù)據(jù)發(fā)送給目標觀察者
@Override
public void call() {
long missed = 1L;
long currentEmission = emitted;
final Queue<Object> q = this.queue;
final Subscriber<? super T> localChild = this.child;
final NotificationLite<T> localOn = this.on;
for (;;) {
while (requestAmount != currentEmission) {
...
localChild.onNext(localOn.getValue(v));
}
}
}
}
}
ObserveOnSubscriber代理觀察者相當(dāng)于微信代理商,讓代理商幫忙買onNext轴术,onCompleted难衰,onError,代理商分別代表你進行購買逗栽。我們看到Worker類的 recursiveScheduler執(zhí)行recursiveScheduler.schedule(this)盖袭,回調(diào)到方法call中。call可以由傳進來的schedule 實現(xiàn)線程切換彼宠。就像上邊的NewThreadWorker.schedule(Action0)一樣鳄虱。
代理的OnSubscribe中的call方法就是讓代理Subscriber訂閱上一級Observable,直到訂閱到原始Observable發(fā)射數(shù)據(jù)凭峡,代理Subscriber收到數(shù)據(jù)后拙已,可能對數(shù)據(jù)做一些操作,然后將數(shù)據(jù)傳送給下一級Subscriber摧冀,直到目標觀察者接收到數(shù)據(jù)倍踪,目標觀察者在那個線程接受數(shù)據(jù)取決于上一個Subscriber在哪一個線程調(diào)用目標觀察者。
嗯索昂,本人技術(shù)有限建车,也是參考以下文章學(xué)習(xí)的。也是希望今年能夠耐心椒惨、堅持學(xué)下去吧缤至。希望在2018年想找個好工作的小伙伴們,共勉康谆、堅持凄杯!
給 Android 開發(fā)者的 RxJava 詳解
https://blog.csdn.net/xmxkf/article/details/51821940