學(xué)習(xí)資料
錯(cuò)誤操作符不多川无,文檔上暫時(shí)是就兩種妇菱,但感覺蠻實(shí)用的承粤,但兩種操作符有好多種延伸
1. Retry重試
當(dāng)原始Observable_Ob
遇到錯(cuò)誤時(shí)暴区,,接收到onError
信號(hào)時(shí)辛臊,會(huì)重新訂閱Ob
仙粱,直到能正常進(jìn)行操作終止,操作符默認(rèn)在trampoline
調(diào)度器上執(zhí)行
而repeat
是接到onCompleted
成功信號(hào)后,重復(fù)訂閱
retry
是失敗后再次訂閱彻舰,repeat
是成功后重復(fù)再來一遍
1.1 retry()
只要收到onError()
的通知伐割,就會(huì)重新訂閱并發(fā)射原始Observable
,無論多少次刃唤。
簡單使用:
/**
* 當(dāng)為4時(shí)隔心,制造一個(gè)異常,重新開始尚胞,就重復(fù)
*/
private static void retry() {
Observable
.just(1, 2, 3, 4, 5)
//將數(shù)據(jù)進(jìn)行變換 小于4就正常發(fā)送
.map((i) -> (i == 4) ? (i / 0) : (i))
//出現(xiàn)異常硬霍,重復(fù)
.retry()
//對發(fā)送數(shù)據(jù)項(xiàng)進(jìn)行限制,就輸出10個(gè)笼裳,否則會(huì)無限輸出
.limit(10)
//將3個(gè)分一組须尚,緩存進(jìn)List,方便觀察結(jié)果
.buffer(3)
.subscribe(new Subscriber<List<Integer>>() {
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
@Override
public void onError(Throwable e) {
System.out.println(e.getMessage());
}
@Override
public void onNext(List<Integer> integers) {
System.out.println(integers);
}
});
}
運(yùn)行結(jié)果:
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1]
onCompleted
結(jié)束時(shí)侍咱,會(huì)得到onCompleted
信號(hào),并不會(huì)得到異常信息
1.2 retry(long)
- retry(count)
當(dāng)收到onError()
的通知時(shí)密幔,最多重復(fù)進(jìn)行count
次訂閱
簡單使用:
/**
* 出現(xiàn)異常楔脯,就重復(fù)訂閱5次
*/
private static void retryCount() {
Observable
.just(1, 2, 3, 4, 5)
//將數(shù)據(jù)進(jìn)行變換
.map((i) -> (i == 4) ? (i / 0) : (i))
//重復(fù)5次
.retry(5)
//3個(gè)數(shù)據(jù)一組
.buffer(3)
.subscribe(new Subscriber<List<Integer>>() {
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
@Override
public void onError(Throwable e) {
System.out.println(e.getMessage());
}
@Override
public void onNext(List<Integer> integers) {
System.out.println(integers);
}
});
}
運(yùn)行結(jié)果:
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
/ by zero
當(dāng)重復(fù)訂閱次數(shù)結(jié)束時(shí),如果異常仍然為解決胯甩,便會(huì)拋出異常昧廷,可以拿到異常的信息,不會(huì)得到onCompleted
信號(hào)
1.3 retry(Func2)
傳入一個(gè)Func2
偎箫,通過函數(shù)來控制更加定制化的重復(fù)訂閱
/**
* 傳入一個(gè)函數(shù)木柬,可以控制重復(fù)訂閱次數(shù)以及是否發(fā)送onError信號(hào)通知
*/
private static void retryFunc() {
Observable
.just(1, 2, 3, 4, 5)
//將數(shù)據(jù)進(jìn)行變換
.map((i) -> (i == 4) ? (i / 0) : (i))
.retry(new Func2<Integer, Throwable, Boolean>() {
//integer 已重復(fù)次數(shù)
//throwable 異常
@Override
public Boolean call(Integer integer, Throwable throwable) {
//System.out.println(integer + throwable.getMessage());
//返回結(jié)果 true 重復(fù)訂閱; false 淹办,不再訂閱眉枕,并將onError通知發(fā)送給訂閱者;null怜森,不再訂閱速挑,不接收onError通知
//重復(fù)次數(shù)小于5 就再次訂閱,超過5次副硅,就結(jié)束姥宝,不再訂閱
return integer<= 5 ? true :null;
}
})
.buffer(3)
.subscribe(new Subscriber<List<Integer>>() {
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
@Override
public void onError(Throwable e) {
System.out.println(e.getMessage());
}
@Override
public void onNext(List<Integer> integers) {
System.out.println(integers);
}
});
}
運(yùn)行結(jié)果:
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
1.4 retryWhen
學(xué)習(xí)資料:
retryWhen
和retry
功能類似,都是在接到 onError
信號(hào)通知的時(shí)候進(jìn)行重新訂閱恐疲,默認(rèn)在trampoline
調(diào)度器上執(zhí)行
retryWhen
會(huì)將作為onError
信號(hào)的Throwable
傳遞給一個(gè)函數(shù)腊满,函數(shù)會(huì)返回一個(gè)新的Observable
套么,新的Observable
發(fā)送出一個(gè)數(shù)據(jù)項(xiàng),retryWhen
就會(huì)重新訂閱原始的Observable
碳蛋;如果新的Observable
發(fā)出了onError
信號(hào)胚泌,則不再重新訂閱
簡單使用:
/**
* 出現(xiàn)異常時(shí),重復(fù)訂閱5次疮蹦,并延遲 2 ^ integer 秒訂閱
*/
private static void retryWhen() {
Observable
.just(1, 2, 3, 4, 5)
.map((i) -> (i == 4) ? (i / 0) : (i))
.retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
@Override
public Observable<?> call(Observable<? extends Throwable> observable) {
return observable
.zipWith(Observable.range(1, 5), new Func2<Throwable, Integer, Integer>() {
@Override
public Integer call(Throwable throwable, Integer i) {
System.out.println(i + " -- " + throwable.getMessage());
return i;
}
})
.flatMap(new Func1<Integer, Observable<?>>() {
@Override
public Observable<?> call(Integer integer) {
//System.out.println(Math.pow(2, integer));
return Observable
.timer((long) Math.pow(2, integer), TimeUnit.SECONDS, Schedulers.immediate());
}
});
}
})
.buffer(3)
.subscribe(new Subscriber<List<Integer>>() {
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
@Override
public void onError(Throwable e) {
System.out.println(e.getMessage());
}
@Override
public void onNext(List<Integer> integers) {
System.out.println(integers);
}
});
}
運(yùn)行結(jié)果:
1 -- / by zero
[1, 2, 3]
2 -- / by zero
[1, 2, 3]
3 -- / by zero
[1, 2, 3]
4 -- / by zero
[1, 2, 3]
5 -- / by zero
onCompleted
在小鄧子大神博客最后提到一個(gè)二進(jìn)制指數(shù)退避算法
诸迟,Math.pow(2, integer)
,延遲2^integer
秒愕乎,重復(fù)訂閱次數(shù)越多阵苇,延遲就越久
算法規(guī)則:
- 對每個(gè)數(shù)據(jù)幀,當(dāng)?shù)谝淮伟l(fā)生沖突時(shí)感论,設(shè)置一個(gè)參數(shù)L=2
- 退避間隔取1到L個(gè)時(shí)間片中的一個(gè)隨機(jī)數(shù)绅项,一個(gè)時(shí)間片等于兩個(gè)節(jié)點(diǎn)之間最大傳播時(shí)延的兩倍
- 當(dāng)數(shù)據(jù)幀再次發(fā)生沖突,則將參量L加倍
- 設(shè)置一個(gè)最大重傳次數(shù)比肄,超過該次數(shù)快耿,則不再重傳,并報(bào)告出錯(cuò)
二進(jìn)制指數(shù)退避算法是按后進(jìn)先出的次序控制的芳绩,即未發(fā)生沖突或很少發(fā)生沖突的數(shù)據(jù)幀掀亥,具有優(yōu)先發(fā)送的概率;而發(fā)生過多次沖突的數(shù)據(jù)幀妥色,發(fā)送成功的概率就更小
每個(gè)字都認(rèn)識(shí)搪花,組在一起,不懂嘹害,先記下來撮竿,知道有這么個(gè)玩意
2. Catch
操作符攔截原始Observable
的onError
通知,將onError
通知替換為其它的數(shù)據(jù)項(xiàng)或數(shù)據(jù)序列笔呀,讓產(chǎn)生的Observable
能夠正常終止或者根本不終止
2.1 onErrorReturn
在接到onError
錯(cuò)誤通知時(shí)幢踏,發(fā)送一個(gè)特殊的數(shù)據(jù)項(xiàng)后立即正常結(jié)束
簡單使用:
/**
* 出現(xiàn)異常時(shí),返回10
*/
private static void catchReturnf() {
Observable
.just(1, 2, 3, 4, 5)
.map((i) -> (i == 4) ? (i / 0) : (i))
.onErrorReturn(new Func1<Throwable, Integer>() {
@Override
public Integer call(Throwable throwable) {
return 10;
}
})
.buffer(3)
.subscribe(System.out::println);
}
運(yùn)行結(jié)果:
[1, 2, 3]
[10]
2.2 onErrorResumeNext
原始Observable
遇到錯(cuò)誤發(fā)送onError
關(guān)閉信號(hào)時(shí)许师,通過一個(gè)函數(shù)創(chuàng)建一個(gè)新的Observable
簡單使用:
/**
* 遇到異常時(shí)房蝉,發(fā)送一個(gè)新的Observable的數(shù)據(jù)項(xiàng)
*/
private static void catchErrorNext() {
Observable
.just(1, 2, 3, 4, 5)
.map((i) -> (i == 4) ? (i / 0) : (i))
.onErrorResumeNext(new Func1<Throwable, Observable<? extends Integer>>() {
@Override
public Observable<? extends Integer> call(Throwable throwable) {
return Observable.just(6,7,8,9);
}
})
.buffer(3)
.subscribe(System.out::println);
}
運(yùn)行結(jié)果:
[1, 2, 3]
[6, 7, 8]
[9]
2.3 onExceptionResumeNext
在接到onError
錯(cuò)誤信號(hào)Throwable_T
時(shí)進(jìn)行判斷:
- 若
T
是一個(gè)Exception
異常 不是一個(gè)錯(cuò)誤就會(huì)啟用onExceptionResumeNext
新生成的備用Obsvable
- 若
T
是一個(gè)Error
錯(cuò)誤,則將這個(gè)錯(cuò)誤發(fā)送到訂閱者的onError()
方法微渠,不會(huì)使用新生成的備用Observable
簡單使用:
/**
* 異常時(shí)惨驶,啟用備用Observable
*/
private static void catchExceptionNext() {
Observable
.just(1, 2, 3, 4, 5)
.map((i) -> (i == 4) ? (i / 0) : (i))
.onExceptionResumeNext(Observable.just(6,7,8))
.buffer(3)
.subscribe(System.out::println);
}
運(yùn)行結(jié)果:
[1, 2, 3]
[6, 7, 8]
3. 最后
重點(diǎn)是retryWhen
的理解,在處理一些過期token
時(shí)敛助,會(huì)用到粗卜。我們的項(xiàng)目中原本token
有效期為半個(gè)小時(shí),后來改成了一個(gè)星期纳击,還沒具體實(shí)踐過
本人很菜续扔,有錯(cuò)誤請指出
共勉 :)