簡單介紹一下RxJava 2.0的多種流:
1.Completable
特性:這個流沒有數據,只會收到error或者complete
示例:
Completable.complete()
.subscribe(() -> printThread("on complete 1"));
Completable.error(new Callable<Throwable>() {
@Override
public Throwable call() throws Exception {
// TODO Auto-generated method stub
return new NullPointerException();
}
})
.subscribe(() -> printThread("on complete 2"),
(e) -> printThread("on error 2 [" + e.getMessage() + "]"));
輸出:
on complete 1[main]
on error 2 [null][main]
2.Single
特性:這個流只會收到一個數據或者一個error,也就是要不然執行onSuccess要不然就執行onError
示例:
Single.just(1)
.subscribe(i -> printThread(String.valueOf(i)));
System.out.println("==============");
Single.fromCallable(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
return 1 / 0;
}
}).subscribe((i, error) -> {
printThread(String.valueOf(i));
printThread(String.valueOf(error));
});
輸出:
1[main]
==============
null[main]
java.lang.ArithmeticException: / by zero[main]
可以看到,正常數據下,收到了數據1,出錯的時候,只會收到一個error。
我們看Single的訂閱接口SingleObserver,如下:
public interface SingleObserver<T> {
void onSubscribe(Disposable d);
void onSuccess(T value);
void onError(Throwable e);
}
只會存在2種回調,符合我們的打印輸出。(上面示例代碼僅僅調用的是簡單的單個情況訂閱,查看源碼,最終都封裝成了SingleObserver)
3.Maybe
特性:和Single類似正常流程也是只執行onSuccess,但在出現錯誤的時候,可以選擇是執行onError還是onComplete
示例(正常流程):
Maybe.just(1)
.subscribe(i -> printThread("success " + i),
(e) -> printThread("error " + e),
() -> printThread("complete"));
輸出:
success 1[main]
示例(錯誤):
Maybe.fromCallable(() -> {
return 1 / 0;
}).subscribe(i -> printThread("success " + i),
(e) -> printThread("error " + e),
() -> printThread("complete"));
輸出:
error java.lang.ArithmeticException: / by zero[main]
我們調用onErrorComplete干預:
Maybe.fromCallable(() -> {
return 1 / 0;
})
.onErrorComplete()
.subscribe(i -> printThread("success " + i),
(e) -> printThread("error " + e),
() -> printThread("complete"));
輸出:
complete[main]
4.Flowable
和Observable功能幾乎一模一樣,區別在于:
1.定義的類功能不一樣
Observable 的訂閱者是:
public interface Observer<T> {
void onSubscribe(Disposable d);
void onNext(T value);
void onError(Throwable e);
void onComplete();
}
public interface Disposable {
void dispose();
boolean isDisposed();
}
Flowable的訂閱者是:
public interface Subscriber<T> {
public void onSubscribe(Subscription s);
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
public interface Subscription {
public void request(long n);
public void cancel();
}
2.Flowable可以通過Subscription對象,調用request(n),響應式拉取數據,來支持背壓特性
示例代碼:
private static int count = 1;
private static boolean isDataEnd() {
return count > 1000;
}
private static void test() {
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> e) throws Exception {
while (!isDataEnd() && !e.isCancelled()) {// 生產數據條件
while (e.requested() <= 0) {// 如果e.request值是0,說明消費者還沒有消費完畢,我們就休息
Thread.sleep(1000);
}
printThread(String.format("OUT生產數據[%d]", count));
e.onNext(count++);
}
e.onComplete();
}
}, BackpressureStrategy.BUFFER)
.subscribe(new Subscriber<Integer>() {
private Subscription mSub;
@Override
public void onComplete() {
// TODO Auto-generated method stub
printThread("消費完畢");
unloopMain();
}
@Override
public void onError(Throwable arg0) {
// TODO Auto-generated method stub
printThread(arg0.getMessage());
unloopMain();
}
@Override
public void onNext(Integer value) {
// TODO Auto-generated method stub
try {
Thread.sleep(100);
printThread(String.format("IN消費數據[%d]", value));
mSub.request(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onSubscribe(Subscription arg0) {
// TODO Auto-generated method stub
mSub = arg0;
mSub.request(1);
}
});
loopMain();
}
private static void loopMain() {
do {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (needBreak)
break;
} while (true);
System.out.println("END");
}
private static void unloopMain() {
needBreak = true;
}
輸出如下:
OUT生產數據[1][main]
IN消費數據[1][main]
OUT生產數據[2][main]
IN消費數據[2][main]
OUT生產數據[3][main]
IN消費數據[3][main]
OUT生產數據[4][main]
IN消費數據[4][main]
OUT生產數據[5][main]
流程分析:
1.調用Flowable.create生產我們的數據流,里面有個新的類型,如下:
public interface FlowableEmitter<T> extends Emitter<T> {
void setDisposable(Disposable s);
void setCancellable(Cancellable c);
long requested();
boolean isCancelled();
FlowableEmitter<T> serialize();
}
其他方法和Observable發射器的功能類似,我們主要需要requested()方法,獲取當前請求個數,如果為0代表還在消費數據,不需要新的數據,我們就休息。
2.BackpressureStrategy,代表支持背壓的策略,如下:
public enum BackpressureStrategy {
//結合onBackpressureXXX()才會生效
MISSING,//在onSubcription時候,s.request(Long.MAX_VALUE);設置最大值。導致一直生產數據。
//直接拋出異常,如果數據溢出
ERROR,
//所有數據會保存到緩存里面
BUFFER,
//丟棄最新的
DROP,
//保存最新的,覆蓋老的
LATEST
}
3.背壓策略,一定要在線程變換之前去調用,線程變換后,收到的訂閱者發生了變化,不是同一個。上面代碼,因為是同一個線程,調用request(1),將直接設置上游數據生產者FlowableEmitter的值為1,但如果是切換線程了,將無法直接影響,增加代碼如下:
Flowable.create(...)
.observeOn(Schedulers.computation())//切換線程
.subscribe(...);
打印如下:
...
OUT生產數據[127][requested = 2][main]
OUT生產數據[128][requested = 1][main]
IN消費數據[1][RxComputationThreadPool-1]
IN消費數據[2][RxComputationThreadPool-1]
IN消費數據[3][RxComputationThreadPool-1]
IN消費數據[4][RxComputationThreadPool-1]
...
我們可以看到,下游的request(1)并不會影響上游的值,上游使用了默認值128的緩存大小。先生產了128個數據,再開始消費。兩個疑問解答:
1.128怎么來的,在調用BackpressureStrategy.Buffer時候,生成的FlowableEmitter實際類型是BufferAsyncEmitter,它默認值就是128
2.我們怎么去控制這個值呢,既然下游影響不到這個大小,可以通過如下代碼:
...
.observeOn(Schedulers.computation(), false, 4)
...
打印輸出,如下:
...
OUT生產數據[1][requested = 4][main]
OUT生產數據[2][requested = 3][main]
OUT生產數據[3][requested = 2][main]
OUT生產數據[4][requested = 1][main]
IN消費數據[1][RxComputationThreadPool-1]
IN消費數據[2][RxComputationThreadPool-1]
IN消費數據[3][RxComputationThreadPool-1]
IN消費數據[4][RxComputationThreadPool-1]
OUT生產數據[5][requested = 3][main]
OUT生產數據[6][requested = 2][main]
OUT生產數據[7][requested = 1][main]
IN消費數據[5][RxComputationThreadPool-1]
IN消費數據[6][RxComputationThreadPool-1]
IN消費數據[7][RxComputationThreadPool-1]
...
這里看到,我們只生產了4個數據,就消費了,以后就是生產3個,這是因為在我們調用observeOn()生成的內部對象FlowableObserveOn里面有個limit = prefetch - (prefetch >> 2); prefetch實際就是傳入的buffersize。如下:
void runAsync(){
...
e++;
if (e == limit) {
if (r != Long.MAX_VALUE) {
r = requested.addAndGet(-e);
}
s.request(e);
e = 0L;
}
}
這個方法在內部類 FlowableObserveOn$ObserveOnSubscriber,我們異步調用sub.request(n)將最終觸發到runAsync(),它會去設置requested也就是上游的數據,所以這個n將無法直接反應到上游,而同步的n是直接設置給上游了
分析線程變換:
使用最簡單的Single來進行探究,代碼如下:
private static void syncRx(){
Single.fromCallable(() -> {
printThread("生產數據");
return "s";
})
.subscribe((s) -> {
printThread("消費數據");
});
}
private static void printThread(String msg){
System.out.println(String.format("[%s][%s]", msg, Thread.currentThread().getName()));
}
打印輸出:
[生產數據][main]
[消費數據][main]
查看源碼,Single.fromCallable 生成的就是SingleFromCallable對象,訂閱表達式生成的是ConsumerSingleObserver對象,代碼變換如下:
private static void syncChangeRx() {
new SingleFromCallable<String>(() -> {
printThread("生產數據");
return "s";
}).subscribe(new ConsumerSingleObserver<>((s) -> {
printThread("消費數據");
}, Functions.ERROR_CONSUMER));
}
輸出打印和上面一模一樣,所以點開方法subscribe(),如下:
public final void subscribe(SingleObserver<? super T> subscriber) {
...
try {
subscribeActual(subscriber);
} catch (NullPointerException ex) {
throw ex;
} catch (Throwable ex) {
...
}
}
最終調用的是抽象方法 subscribeActual(),而我們知道我們來源于SingleFromCallable,所以實際實現在SingleFromCallable.subscribeActual()方法里面,如下:
@Override
protected void subscribeActual(SingleObserver<? super T> s) {
s.onSubscribe(EmptyDisposable.INSTANCE);
try {
T v = callable.call();
if (v != null) {
s.onSuccess(v);
} else {
s.onError(new NullPointerException("The callable returned a null value"));
}
} catch (Throwable e) {
...
}
}
里面也很簡單,參數 s,就是我們自己定義的后生產的ConsumerSingleObserver對象,callable就是我們定義的生產對象,所以下游的訂閱動作,如下:
1.訂閱觸發流程
2.進入真實SingleFromCallable.subscribeActual()
3.調用callable.call()生產數據或者異常
4.回調給ConsumerSingleObserver,我們自己的消費者
5.完成結束
以上就是最簡單的單線程調用了,在以上的基礎上,我們增加一個線程切換,如下:
private static void asyncChangeRx() {
new SingleFromCallable<String>(() -> {
printThread("生產數據");
return "s";
})
.observeOn(Schedulers.computation())
.subscribe(new ConsumerSingleObserver<>((s) -> {
printThread("消費數據");
}, Functions.ERROR_CONSUMER));
}
打印輸出:
[生產數據][main]
[消費數據][RxComputationThreadPool-1]
查看源碼,我們可以知道,observeOn也生產了一個新的包裝流SingleObserveOn,變換,如下:
private static void asyncChangeRx() {
SingleSource<String> producer = new SingleFromCallable<>(() -> {
printThread("生產數據");
return "s";
});
SingleObserver<String> consumer = new ConsumerSingleObserver<>((s) -> {
printThread("消費數據");
}, Functions.ERROR_CONSUMER);
new SingleObserveOn<>(producer, Schedulers.computation())
.subscribe(consumer);
}
這樣也就是具有線程變換功能的SingleObserveOn,包裹起了原始的生產者SingleFromCallable,其他不變,因此我們先認為是單一線程模型可以大概推出:
consumer訂閱 --->
SingleObserveOn.subscribeActual() --->
SingleFromCallable.subscribeActual()
所以真正線程變換就在SingleObserveOn.subscribeActual()里面,實現如下:
@Override
protected void subscribeActual(final SingleObserver<? super T> s) {
source.subscribe(new ObserveOnSingleObserver<T>(s, scheduler));
}
這里SingleObserver<? super T> s就是我們自己的 consumer, 而source就是我們的 producer,一定要注意,上面這個訂閱過程會導致上游產生數據,因此將觸發ObserveOnSingleObserver.onSuccess(T t),我們再看具體實現代碼,如下:
static final class ObserveOnSingleObserver<T> extends AtomicReference<Disposable>
implements SingleObserver<T>, Disposable, Runnable {
...
final SingleObserver<? super T> actual;//我們實際的訂閱者
final Scheduler scheduler;//我們設定的線程執行類
T value;//保存上游的數據
Throwable error;//保存上游的錯誤
@Override
public void onSuccess(T value) {
this.value = value;//拿到上游數據
Disposable d = scheduler.scheduleDirect(this);//要求線程執行自己
DisposableHelper.replace(this, d);
}
@Override
public void onError(Throwable e) {
this.error = e;
Disposable d = scheduler.scheduleDirect(this);
DisposableHelper.replace(this, d);
}
@Override
public void run() {//這里異步執行原有的調用流程
Throwable ex = error;
if (ex != null) {
actual.onError(ex);
} else {
actual.onSuccess(value);
}
}
...
}
生產數據后,ObserveOnSingleObserver本身繼承了Runnable,將同步調用的流程封裝在了run()方法里面,再叫scheduler去執行自己,完成了線程切換。
線程切換最簡單的整個流程,就是以上調用,如果加上生產者也要切換線程,也是一樣的,它有個對象,SingleSubscribeOn來包裝流,包裝過程偽代碼如下:
從調用鏈最后開始,往上包裝:
SingleObserveOn --包含-->
SingleSubscribeOn --包含-->
producer
訂閱過程,又最后往上 被包裝:
consumer --被包含-->
ObserveOnSingleObserver--被包含--> 負責消費者切換
SubscribeOnObserver 負責生產者切換
SubscribeOnObserver 里面也很簡單,也是run方法,如下:
@Override
public void run() {
source.subscribe(this);//訂閱就是生產數據
}
線程變換總結:
1.訂閱將會觸發生產
2.將上游的訂閱過程,封裝到runnable,再交由scheduler去執行
3.將下游的消費過程,封裝到runnable,再交由scheduler去執行
其他思考:
多次線程變換,生產數據會在哪次里面?,而消費過程會在哪次里面?
示例代碼:
public static void main(String[] args) {
asyncChangeRx2();
loopMain();
}
private static void asyncChangeRx2() {
Single.fromCallable(() -> {
printThread("生產數據");
return "s";
})
.subscribeOn(myScheduler("生產包裝線程1"))
.subscribeOn(myScheduler("生產包裝線程2"))
.observeOn(myScheduler("消費包裝線程1"))
.observeOn(myScheduler("消費包裝線程2"))
.subscribe((s) -> {
printThread("消費數據");
unloopMain();
});
}
private static Scheduler myScheduler(String name) {
return new Scheduler() {
@Override
public Worker createWorker() {
// TODO Auto-generated method stub
return new NewThreadWorker(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
// TODO Auto-generated method stub
return new Thread(new HookRun(r), name);
}
});
}
};
}
//簡單打印活動線程
private static class HookRun implements Runnable {
private Runnable mRealRun;
public HookRun(Runnable r) {
// TODO Auto-generated constructor stub
mRealRun = r;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(String.format("執行Run[%s]", Thread.currentThread().getName()));
mRealRun.run();
}
}
private static void printThread(String msg) {
System.out.println(String.format("[%s][%s]", msg, Thread.currentThread().getName()));
}
private static void loopMain() {
do {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (needBreak)
break;
} while (true);
System.out.println("END");
}
private static void unloopMain() {
needBreak = true;
}
private static boolean needBreak;
}
輸出:
執行Run[生產包裝線程2]
執行Run[生產包裝線程1]
[生產數據][生產包裝線程1]
執行Run[消費包裝線程1]
執行Run[消費包裝線程2]
[消費數據][消費包裝線程2]
END
說明生產最終在第一次包裝里面,消費在最后一次包裝里面,符合我們剛才分析的包裝過程偽代碼的方向。離活動(生產或者消費)最近的一次線程切換包裝負責執行