觀(guān)察者模式
場(chǎng)景描述
先看一個(gè)關(guān)注微信公眾號(hào)的業(yè)務(wù),關(guān)注完微信公眾號(hào)之后要進(jìn)行如下操作:
1、記錄文本日志
2、記錄數(shù)據(jù)庫(kù)日志
3、發(fā)送短信
4、送優(yōu)惠券,積分等
5、其他各類(lèi)活動(dòng)等
傳統(tǒng)解決方案:
在關(guān)注公眾號(hào)邏輯等類(lèi)內(nèi)部增加相關(guān)代碼,完成各種邏輯。
存在問(wèn)題:
1、一旦某個(gè)業(yè)務(wù)邏輯發(fā)生改變,如關(guān)注公眾號(hào)業(yè)務(wù)中增加其他業(yè)務(wù)邏輯,需要修改關(guān)注公眾號(hào)核心文件、甚至關(guān)注流程。
2、日積月累后,文件冗長(zhǎng),導(dǎo)致后續(xù)維護(hù)困難。
存在問(wèn)題原因主要是程序的"緊密耦合",使用觀(guān)察模式可以將目前的業(yè)務(wù)邏輯優(yōu)化成"松耦合",達(dá)到易維護(hù)、易修改的目的,同時(shí)也符合面向接口編程的思想。
什么是觀(guān)察者模式
觀(guān)察者模式(Observer Pattern):定義對(duì)象間的一種一對(duì)多依賴(lài)關(guān)系,使得每當(dāng)一個(gè)對(duì)象狀態(tài)發(fā)生改變時(shí),其相關(guān)依賴(lài)對(duì)象皆得到通知并被自動(dòng)更新。觀(guān)察者模式又叫做發(fā)布-訂閱(Publish/Subscribe)模式。觀(guān)察者模式是一種對(duì)象行為型模式。
各個(gè)字段含義如下:
- Subject: 目標(biāo),抽象被觀(guān)察者。 抽象主題角色把所有觀(guān)察者對(duì)象保存在一個(gè)集合里,每個(gè)主題都可以有任意數(shù)量的觀(guān)察者,抽象主題提供一個(gè)接口,可以增加和刪除觀(guān)察者對(duì)象。
- ConcreteSubject: 具體目標(biāo),具體的被觀(guān)察者。 該角色將有關(guān)狀態(tài)存入具體觀(guān)察者對(duì)象,在具體主題的內(nèi)部狀態(tài)發(fā)生改變時(shí),給所有注冊(cè)過(guò)的觀(guān)察者發(fā)送通知。
- Observer: 觀(guān)察者。 是觀(guān)察者者的抽象類(lèi),它定義了一個(gè)更新接口,使得在得到主題更改通知時(shí)更新自己。
- ConcreteObserver: 具體觀(guān)察者。實(shí)現(xiàn)抽象觀(guān)察者定義的更新接口,以便在得到主題更改通知時(shí)更新自身的狀態(tài)。
觀(guān)察者模式的簡(jiǎn)單實(shí)現(xiàn)
觀(guān)察者模式這種發(fā)布-訂閱的形式我們可以拿微信公眾號(hào)來(lái)舉例,假設(shè)微信用戶(hù)就是觀(guān)察者,微信公眾號(hào)是被觀(guān)察者,有多個(gè)的微信用戶(hù)關(guān)注了程序猿這個(gè)公眾號(hào),當(dāng)這個(gè)公眾號(hào)更新時(shí)就會(huì)通知這些訂閱的微信用戶(hù)。看看用代碼如何實(shí)現(xiàn):
抽象觀(guān)察者(Observer)
//抽象觀(guān)察者
public interface Observer {
//收到訂閱消息執(zhí)行的操作
void update(String message);
}
具體觀(guān)察者(ConcrereObserver)
//記錄日志
public class LogObserver implements Observer{
@Override
public void update(String message) {
System.out.println("日志訂閱---"+message+"---開(kāi)始記錄日志");
}
}
//優(yōu)惠券訂閱
public class TicketObserver implements Observer{
@Override
public void update(String message) {
System.out.println("優(yōu)惠券訂閱---"+message+"---開(kāi)始發(fā)送優(yōu)惠券");
}
}
抽象被觀(guān)察者(Subject)
//抽象被觀(guān)察者
public interface Subject {
/**
- 增加訂閱者
- @param observer
*/
void attach(Observer observer);
/**
- 刪除訂閱者
- @param observer
*/
void detach(Observer observer);
/**
- 通知訂閱者
*/
void notify(String message);
}
具體被觀(guān)察者(ConcreteSubject)
//訂閱公眾號(hào)
public class SubscriptionSubject implements Subject {
//儲(chǔ)存訂閱公眾號(hào)的業(yè)務(wù)
private List<Observer> list= new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
list.add(observer);
}
@Override
public void detach(Observer observer) {
list.remove(observer);
}
@Override
public void notify(String message) {
for (Observer observer : list) {
observer.update(message);
}
}
public void subscribe(){
System.out.println("執(zhí)行訂閱的具體操作");
notify("訂閱成功了");
}
}
客戶(hù)端調(diào)用
public class Client {
public static void main(String[] args) {
SubscriptionSubject subscriptionSubject = new SubscriptionSubject();
//添加日志訂閱
Observer logObserver = new LogObserver();
subscriptionSubject.attach(logObserver);
//添加優(yōu)惠券訂閱
TicketObserver ticketObserver = new TicketObserver();
subscriptionSubject.attach(ticketObserver);
//執(zhí)行訂閱的操作
subscriptionSubject.subscribe();
}
}
執(zhí)行結(jié)果
執(zhí)行訂閱的具體操作
日志訂閱---訂閱成功了---開(kāi)始記錄日志
優(yōu)惠券訂閱---訂閱成功了---開(kāi)始發(fā)送優(yōu)惠券
使用觀(guān)察者模式的場(chǎng)景和優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn)
解除耦合,讓耦合的雙方都依賴(lài)于抽象,從而使得各自的變換都不會(huì)影響另一邊的變換。
- 缺點(diǎn)
在應(yīng)用觀(guān)察者模式時(shí)需要考慮一下開(kāi)發(fā)效率和運(yùn)行效率的問(wèn)題,程序中包括一個(gè)被觀(guān)察者、多個(gè)觀(guān)察者,開(kāi)發(fā)、調(diào)試等內(nèi)容會(huì)比較復(fù)雜,而且在Java中消息的通知一般是順序執(zhí)行,那么一個(gè)觀(guān)察者卡頓,會(huì)影響整體的執(zhí)行效率,在這種情況下,一般會(huì)采用異步實(shí)現(xiàn)。
java中的實(shí)現(xiàn)
JDK 提供了 一套 觀(guān)察者模式的實(shí)現(xiàn),在java.util包中, java.util.Observable類(lèi)和java.util.Observer接口。Observable是被觀(guān)察者,Observer是觀(guān)察者。
推模型和拉模型
在觀(guān)察者模式中,又分為推模型和拉模型兩種方式。
- 推模型
主題對(duì)象向觀(guān)察者推送主題的詳細(xì)信息,不管觀(guān)察者是否需要,推送的信息通常是主題對(duì)象的全部或部分?jǐn)?shù)據(jù)。
- 拉模型
主題對(duì)象在通知觀(guān)察者的時(shí)候,只傳遞少量信息。如果觀(guān)察者需要更具體的信息,由觀(guān)察者主動(dòng)到主題對(duì)象中獲取,相當(dāng)于是觀(guān)察者從主題對(duì)象中拉數(shù)據(jù)。一般這種模型的實(shí)現(xiàn)中,會(huì)把主題對(duì)象自身通過(guò)update()方法傳遞給觀(guān)察者,這樣在觀(guān)察者需要獲取數(shù)據(jù)的時(shí)候,就可以通過(guò)這個(gè)引用來(lái)獲取了。
根據(jù)上面的描述,發(fā)現(xiàn)上面的例子就是典型的推模型,下面給出一個(gè)拉模型的實(shí)例。
拉模型的抽象主題類(lèi)
拉模型的抽象主題類(lèi)主要的改變是nodifyObservers()方法。在循環(huán)通知觀(guān)察者的時(shí)候,也就是循環(huán)調(diào)用觀(guān)察者的update()方法的時(shí)候,傳入的參數(shù)不同了。
public abstract class Subject {
/**
- 用來(lái)保存注冊(cè)的觀(guān)察者對(duì)象
*/
private List<Observer> list = new ArrayList<Observer>();
/**
- 注冊(cè)觀(guān)察者對(duì)象
@param observer 觀(guān)察者對(duì)象
*/
public void attach(Observer observer) {
list.add(observer);
System.out.println("Attached an observer");
}
/**
- 刪除觀(guān)察者對(duì)象
@param observer 觀(guān)察者對(duì)象
*/
public void detach(Observer observer) {
list.remove(observer);
}
/**
- 通知所有注冊(cè)的觀(guān)察者對(duì)象
*/
public void nodify() {
for (Observer observer : list) {
observer.update(this);
}
}
}
拉模型的具體主題類(lèi)
跟推模型相比,有一點(diǎn)變化,就是調(diào)用通知觀(guān)察者的方法的時(shí)候,不需要傳入?yún)?shù)了。
public class ConcreteSubject extends Subject {
private String state;
public String getState() {
return state;
}
public void change(String newState) {
state = newState;
System.out.println("主題狀態(tài)為:" + state);
//狀態(tài)發(fā)生改變,通知各個(gè)觀(guān)察者
this.nodify();
}
}
拉模型的抽象觀(guān)察者類(lèi)
拉模型通常都是把主題對(duì)象當(dāng)做參數(shù)傳遞。
public interface Observer {
/**
- 更新接口
@param subject 傳入主題對(duì)象,方面獲取相應(yīng)的主題對(duì)象的狀態(tài)
*/
void update(Subject subject);
}
拉模型的具體觀(guān)察者類(lèi)
public class ConcreteObserver implements Observer {
//觀(guān)察者的狀態(tài)
private String observerState;
@Override
public void update(Subject subject) {
/**
- 更新觀(guān)察者的狀態(tài),使其與目標(biāo)的狀態(tài)保持一致
*/
observerState = ((ConcreteSubject)subject).getState();
System.out.println("觀(guān)察者狀態(tài)為:"+observerState);
}
}
兩種模式的比較
推模型是假定主題對(duì)象知道觀(guān)察者需要的數(shù)據(jù);而拉模型是主題對(duì)象不知道觀(guān)察者具體需要什么數(shù)據(jù),沒(méi)有辦法的情況下,干脆把自身傳遞給觀(guān)察者,讓觀(guān)察者自己去按需要取值。
推模型可能會(huì)使得觀(guān)察者對(duì)象難以復(fù)用,因?yàn)橛^(guān)察者的update()方法是按需要定義的參數(shù),可能無(wú)法兼顧沒(méi)有考慮到的使用情況。這就意味著出現(xiàn)新情況的時(shí)候,就可能提供新的 update()方法,或者是干脆重新實(shí)現(xiàn)觀(guān)察者;而拉模型就不會(huì)造成這樣的情況,因?yàn)槔P拖拢瑄pdate()方法的參數(shù)是主題對(duì)象本身,這基本上是主題對(duì)象能傳遞的最大數(shù)據(jù)集合了,基本上可以適應(yīng)各種情況的需要。
RxJava
場(chǎng)景描述
假設(shè)有這樣一個(gè)需求:界面上有一個(gè)自定義的視圖 imageCollectorView (圖片展示),它的作用是顯示多張圖片,并能使用 addImage(Bitmap) 方法來(lái)任意增加顯示的圖片。現(xiàn)在需要程序?qū)⒁粋€(gè)給出的目錄數(shù)組 File[] folders 中每個(gè)目錄下的 png 圖片都加載出來(lái)并顯示在 imageCollectorView 中。由于讀取圖片的這一過(guò)程較為耗時(shí),需要放在后臺(tái)執(zhí)行,而圖片的顯示則必須在 UI 線(xiàn)程執(zhí)行。常見(jiàn)的實(shí)現(xiàn)方式,在后臺(tái)開(kāi)啟一個(gè)線(xiàn)程,執(zhí)行加載圖片的操作。代碼如下:
new Thread() {
@Override
public void run() {
super.run();
for (File folder : folders) {
File[] files = folder.listFiles();
for (File file : files) {
if (file.getName().endsWith(".png")) {
final Bitmap bitmap = getBitmapFromFile(file);
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
imageCollectorView.addImage(bitmap);
}
});
}
}
}
}
}.start();
而如果使用 RxJava ,實(shí)現(xiàn)方式是這樣的:
Observable.from(folders)
.flatMap(new Func1<File, Observable<File>>() {
@Override
public Observable<File> call(File file) {
return Observable.from(file.listFiles());
}
})
.filter(new Func1<File, Boolean>() {
@Override
public Boolean call(File file) {
return file.getName().endsWith(".png");
}
})
.map(new Func1<File, Bitmap>() {
@Override
public Bitmap call(File file) {
return getBitmapFromFile(file);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) {
imageCollectorView.addImage(bitmap);
}
});
從中可以看出rxjava代碼邏輯的簡(jiǎn)潔。rxjava代碼簡(jiǎn)潔不是指代碼量少,而是指代碼邏輯的簡(jiǎn)潔。
什么是RxJava
原文 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個(gè)在 Java VM 上使用可觀(guān)測(cè)的序列來(lái)組成異步的、基于事件的程序的庫(kù))。這就是 RxJava ,概括得非常精準(zhǔn)。
其實(shí), RxJava 的本質(zhì)可以壓縮為異步這一個(gè)詞。說(shuō)到根上,它就是一個(gè)實(shí)現(xiàn)異步操作的庫(kù)。
RxJava的特點(diǎn)
異步操作很關(guān)鍵的一點(diǎn)是程序的簡(jiǎn)潔性,因?yàn)樵谡{(diào)度過(guò)程比較復(fù)雜的情況下,異步代碼經(jīng)常會(huì)既難寫(xiě)也難被讀懂。 Android 創(chuàng)造的 AsyncTask 和Handler ,其實(shí)都是為了讓異步代碼更加簡(jiǎn)潔。RxJava 的優(yōu)勢(shì)也是簡(jiǎn)潔,但它的簡(jiǎn)潔的與眾不同之處在于,隨著程序邏輯變得越來(lái)越復(fù)雜,它依然能夠保持簡(jiǎn)潔。
RxJava的 觀(guān)察者模式
RxJava 的異步實(shí)現(xiàn),是通過(guò)一種擴(kuò)展的觀(guān)察者模式來(lái)實(shí)現(xiàn)的。
RxJava 的觀(guān)察者模式大致如下圖:
RxJava 有四個(gè)基本概念:
- Observable (被觀(guān)察者) 產(chǎn)生事件
- Observer (觀(guān)察者) 接收事件,并給出響應(yīng)動(dòng)作
- subscribe (訂閱) 連接 被觀(guān)察者 & 觀(guān)察者
- 事件 被觀(guān)察者 & 觀(guān)察者 溝通的載體
Observable 和 Observer 通過(guò) subscribe() 方法實(shí)現(xiàn)訂閱關(guān)系,從而 Observable 可以在需要的時(shí)候發(fā)出事件來(lái)通知Observer。
與傳統(tǒng)觀(guān)察者模式不同, RxJava 的事件回調(diào)方法除了普通事件 onNext() (相當(dāng)于 onClick() / onEvent())之外,還定義了兩個(gè)特殊的事件:onCompleted() 和 onError()。
- onCompleted(): 事件隊(duì)列完結(jié)。RxJava 不僅把每個(gè)事件單獨(dú)處理,還會(huì)把它們看做一個(gè)隊(duì)列。RxJava 規(guī)定,當(dāng)不會(huì)再有新的 onNext() 發(fā)出時(shí),需要觸發(fā) onCompleted() 方法作為標(biāo)志。
- onError(): 事件隊(duì)列異常。在事件處理過(guò)程中出異常時(shí),onError() 會(huì)被觸發(fā),同時(shí)隊(duì)列自動(dòng)終止,不允許再有事件發(fā)出。
在一個(gè)正確運(yùn)行的事件序列中, onCompleted() 和 onError() 有且只有一個(gè),并且是事件序列中的最后一個(gè)。需要注意的是,onCompleted() 和 onError() 二者也是互斥的,即在隊(duì)列中調(diào)用了其中一個(gè),就不應(yīng)該再調(diào)用另一個(gè)。
RxJava的實(shí)現(xiàn)
基于以上的概念, RxJava 的基本實(shí)現(xiàn)主要有三點(diǎn):
- 創(chuàng)建 觀(guān)察者Observer
Observer 即觀(guān)察者,它決定事件觸發(fā)的時(shí)候?qū)⒂性鯓拥男袨椤?RxJava 中的 Observer 接口的實(shí)現(xiàn)方式:
Observer<String> observer = new Observer<String>() {
@Override
public void onNext(String s) {
Log.d(tag, "Item: " + s);
}
@Override
public void onCompleted() {
Log.d(tag, "Completed!");
}
@Override
public void onError(Throwable e) {
Log.d(tag, "Error!");
}
};
- 創(chuàng)建 被觀(guān)察者Observable
Observable 即被觀(guān)察者,它決定什么時(shí)候觸發(fā)事件以及觸發(fā)怎樣的事件。 RxJava 使用 create() 方法來(lái)創(chuàng)建一個(gè) Observable ,并為它定義事件觸發(fā)規(guī)則:
Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("Hello");
subscriber.onNext("Hi");
subscriber.onNext("Aloha");
subscriber.onCompleted();
}
});
可以看到,這里傳入了一個(gè) OnSubscribe 對(duì)象作為參數(shù)。OnSubscribe 會(huì)被存儲(chǔ)在返回的 被觀(guān)察者Observable 對(duì)象中,它的作用相當(dāng)于一個(gè)計(jì)劃表,當(dāng)被觀(guān)察者Observable 被訂閱的時(shí)候,OnSubscribe 的 call() 方法會(huì)自動(dòng)被調(diào)用,事件序列就會(huì)依照設(shè)定依次觸發(fā)(對(duì)于上面的代碼,就是觀(guān)察者Subscriber 將會(huì)被調(diào)用三次 onNext() 和一次 onCompleted())。這樣,由被觀(guān)察者調(diào)用了觀(guān)察者的回調(diào)方法,就實(shí)現(xiàn)了由被觀(guān)察者向觀(guān)察者的事件傳遞,即觀(guān)察者模式。
create() 方法是 RxJava 最基本的創(chuàng)造事件序列的方法。基于這個(gè)方法, RxJava 還提供了一些方法用來(lái)快捷創(chuàng)建事件隊(duì)列,例如:
- just(T...): 將傳入的參數(shù)依次發(fā)送出來(lái)。
Observable observable = Observable.just("Hello", "Hi", "Aloha");
// 將會(huì)依次調(diào)用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
- from(T[]) / from(Iterable<? extends T>) : 將傳入的數(shù)組或 Iterable 拆分成具體對(duì)象后,依次發(fā)送出來(lái)。
String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);
// 將會(huì)依次調(diào)用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
- Subscribe (訂閱)
創(chuàng)建了被觀(guān)察者 Observable 和觀(guān)察者 Observer 之后,再用 subscribe() 方法將它們聯(lián)結(jié)起來(lái),整條鏈子就可以工作了。代碼形式很簡(jiǎn)單:
observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);
整個(gè)過(guò)程中對(duì)象間的關(guān)系如下圖:
基于事件流的鏈?zhǔn)秸{(diào)用
上述的實(shí)現(xiàn)方式是為了說(shuō)明Rxjava的原理 & 使用
在實(shí)際應(yīng)用中,會(huì)將上述步驟&代碼連在一起,從而更加簡(jiǎn)潔、更加優(yōu)雅,即所謂的 RxJava基于事件流的鏈?zhǔn)秸{(diào)用
// RxJava的鏈?zhǔn)讲僮?/p>
Observable.create(new ObservableOnSubscribe<Integer>() {
// 1. 創(chuàng)建被觀(guān)察者 & 生產(chǎn)事件
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
}).subscribe(new Observer<Integer>() {
// 2. 通過(guò)通過(guò)訂閱(subscribe)連接觀(guān)察者和被觀(guān)察者
// 3. 創(chuàng)建觀(guān)察者 & 定義響應(yīng)事件的行為
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "開(kāi)始采用subscribe連接");
}
// 默認(rèn)最先調(diào)用復(fù)寫(xiě)的 onSubscribe()
@Override
public void onNext(Integer value) {
Log.d(TAG, "對(duì)Next事件"+ value +"作出響應(yīng)" );
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "對(duì)Error事件作出響應(yīng)");
}
@Override
public void onComplete() {
Log.d(TAG, "對(duì)Complete事件作出響應(yīng)");
}
});
這種 基于事件流的鏈?zhǔn)秸{(diào)用,使得RxJava:
- 邏輯簡(jiǎn)潔
- 實(shí)現(xiàn)優(yōu)雅
- 使用簡(jiǎn)單
更重要的是,隨著程序邏輯的復(fù)雜性提高,它依然能夠保持簡(jiǎn)潔 & 優(yōu)雅。所以,一般建議使用這種基于事件流的鏈?zhǔn)秸{(diào)用方式實(shí)現(xiàn)RxJava。
線(xiàn)程控制
在 RxJava 的默認(rèn)規(guī)則中,事件的發(fā)出和消費(fèi)都是在同一個(gè)線(xiàn)程的。也就是說(shuō),如果只用上面的方法,實(shí)現(xiàn)出來(lái)的只是一個(gè)同步的觀(guān)察者模式。觀(guān)察者模式本身的目的就是『后臺(tái)處理,前臺(tái)回調(diào)』的異步機(jī)制,因此異步對(duì)于 RxJava 是至關(guān)重要的。而要實(shí)現(xiàn)異步,則需要用到 RxJava 的另一個(gè)概念: Scheduler 。
在不指定線(xiàn)程的情況下, RxJava 遵循的是線(xiàn)程不變的原則,即:在哪個(gè)線(xiàn)程調(diào)用 subscribe(),就在哪個(gè)線(xiàn)程生產(chǎn)事件;在哪個(gè)線(xiàn)程生產(chǎn)事件,就在哪個(gè)線(xiàn)程消費(fèi)事件。如果需要切換線(xiàn)程,就需要用到 Scheduler (調(diào)度器)。
在RxJava 中,Scheduler ——調(diào)度器,相當(dāng)于線(xiàn)程控制器,RxJava 通過(guò)它來(lái)指定每一段代碼應(yīng)該運(yùn)行在什么樣的線(xiàn)程。
略...
感興趣可參考 給 Android 開(kāi)發(fā)者的 RxJava 詳解
場(chǎng)景示例
-
需求場(chǎng)景
- 功能邏輯
- 代碼實(shí)現(xiàn)
public class RxJavaRetry {
private static final String TAG = "RxJava";
// 可重試次數(shù)
private int maxConnectCount = 10;
// 當(dāng)前已重試次數(shù)
private int currentRetryCount = 0;
// 重試等待時(shí)間
private int waitRetryTime = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
//省略
// 步驟3:采用Observable<...>形式 對(duì) 網(wǎng)絡(luò)請(qǐng)求 進(jìn)行封裝
Observable<Translation> observable = request.getCall();
// 步驟4:發(fā)送網(wǎng)絡(luò)請(qǐng)求 & 通過(guò)retryWhen()進(jìn)行重試
// 注:主要異常才會(huì)回調(diào)retryWhen()進(jìn)行重試
observable.retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Observable<Throwable> throwableObservable) throws Exception {
// 參數(shù)Observable<Throwable>中的泛型 = 上游操作符拋出的異常,可通過(guò)該條件來(lái)判斷異常的類(lèi)型
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {
// 輸出異常信息
Log.d(TAG, "發(fā)生異常 = "+ throwable.toString());
/**
- 需求1:根據(jù)異常類(lèi)型選擇是否重試
- 即,當(dāng)發(fā)生的異常 = 網(wǎng)絡(luò)異常 = IO異常 才選擇重試
*/
if (throwable instanceof IOException){
Log.d(TAG, "屬于IO異常,需重試" );
/**
- 需求2:限制重試次數(shù)
- 即,當(dāng)已重試次數(shù) < 設(shè)置的重試次數(shù),才選擇重試
*/
if (currentRetryCount < maxConnectCount){
// 記錄重試次數(shù)
currentRetryCount++;
/**
- 需求2:實(shí)現(xiàn)重試
- 通過(guò)返回的Observable發(fā)送的事件 = Next事件,從而使得retryWhen()重訂閱,最終實(shí)現(xiàn)重試功能
需求3:延遲1段時(shí)間再重試
- 采用delay操作符 = 延遲一段時(shí)間發(fā)送,以實(shí)現(xiàn)重試間隔設(shè)置
需求4:遇到的異常越多,時(shí)間越長(zhǎng)
- 在delay操作符的等待時(shí)間內(nèi)設(shè)置 = 每重試1次,增多延遲重試時(shí)間1s
*/
// 設(shè)置等待時(shí)間
waitRetryTime = 1000 + currentRetryCount* 1000;
return Observable.just(1).delay(waitRetryTime, TimeUnit.MILLISECONDS);
}else{
// 若重試次數(shù)已 > 設(shè)置重試次數(shù),則不重試
// 通過(guò)發(fā)送error來(lái)停止重試(可在觀(guān)察者的onError()中獲取信息)
return Observable.error(new Throwable("重試次數(shù)已超過(guò)設(shè)置次數(shù) = " +currentRetryCount + ",即 不再重試"));
}
}else{
// 若發(fā)生的異常不屬于I/O異常,則不重試
// 通過(guò)返回的Observable發(fā)送的事件 = Error事件 實(shí)現(xiàn)(可在觀(guān)察者的onError()中獲取信息)
return Observable.error(new Throwable("發(fā)生了非網(wǎng)絡(luò)異常(非I/O異常)"));
}
}
});
}
}).subscribeOn(Schedulers.io()) // 切換到IO線(xiàn)程進(jìn)行網(wǎng)絡(luò)請(qǐng)求
.observeOn(AndroidSchedulers.mainThread()) // 切換回到主線(xiàn)程 處理請(qǐng)求結(jié)果
.subscribe(new Observer<Translation>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Translation result) {
// 接收服務(wù)器返回的數(shù)據(jù)
Log.d(TAG, "發(fā)送成功");
result.show();
}
@Override
public void onError(Throwable e) {
// 獲取停止重試的信息
Log.d(TAG, e.toString());
}
@Override
public void onComplete() {
}
});
}
}
更多場(chǎng)景可以參考 使用RxJava的最佳開(kāi)發(fā)場(chǎng)景
HYSTRIX中的觀(guān)察者模式
Hystrix中的命令
Hystrix有兩個(gè)請(qǐng)求命令 HystrixCommand、HystrixObservableCommand。
HystrixCommand用在依賴(lài)服務(wù)返回單個(gè)操作結(jié)果的時(shí)候。又兩種執(zhí)行方式
- execute():同步執(zhí)行。從依賴(lài)的服務(wù)返回一個(gè)單一的結(jié)果對(duì)象,或是在發(fā)生錯(cuò)誤的時(shí)候拋出異常。
- queue();異步執(zhí)行。直接返回一個(gè)Future對(duì)象,其中包含了服務(wù)執(zhí)行結(jié)束時(shí)要返回的單一結(jié)果對(duì)象。
HystrixObservableCommand 用在依賴(lài)服務(wù)返回多個(gè)操作結(jié)果的時(shí)候。它也實(shí)現(xiàn)了兩種執(zhí)行方式
- observe():返回Obervable對(duì)象,他代表了操作的多個(gè)結(jié)果,他是一個(gè)HotObservable
- toObservable():同樣返回Observable對(duì)象,也代表了操作多個(gè)結(jié)果,但它返回的是一個(gè)Cold Observable。
在Hystrix的底層實(shí)現(xiàn)中大量使用了RxJava。上面提到的Observable對(duì)象就是RxJava的核心內(nèi)容之一,可以把Observable對(duì)象理解為事件源或是被觀(guān)察者,與其對(duì)應(yīng)的是Subscriber對(duì)象,可以理解為訂閱者或是觀(guān)察者。
- Observable用來(lái)向訂閱者Subscriber對(duì)象發(fā)布事件,Subscriber對(duì)象在接收到事件后對(duì)其進(jìn)行處理,這里所指的事件通常就是對(duì)依賴(lài)服務(wù)的調(diào)用。
- 一個(gè)Observable可以發(fā)出多個(gè)事件,直到結(jié)束或是發(fā)生異常。
- Observable對(duì)象每發(fā)出一個(gè)事件,就會(huì)調(diào)用對(duì)應(yīng)觀(guān)察者Subscriber對(duì)象的onNext()方法。
- 每一個(gè)Observable的執(zhí)行,最后一定會(huì)通過(guò)調(diào)用Subscriber.onCompleted()或是Subscriber.onError()來(lái)結(jié)束該事件的操作流。
調(diào)用實(shí)例
public class HelloWorldHystrixObservableCommand extends HystrixObservableCommand<String> {
private final String name;
protected HelloWorldHystrixObservableCommand(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
@Override
protected Observable<String> construct() {
System.out.println("in construct! thread:" + Thread.currentThread().getName());
return (Observable<String>) Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> observer) {
try {
System.out.println("in call of construct! thread:" + Thread.currentThread().getName());
if (!observer.isUnsubscribed()) {
// 直接拋異常退出,不會(huì)往下執(zhí)行
// observer.onError(getExecutionException());
observer.onNext("Hello1" + " thread:" + Thread.currentThread().getName());
observer.onNext("Hello2" + " thread:" + Thread.currentThread().getName());
observer.onNext(name + " thread:" + Thread.currentThread().getName());
System.out.println("complete before------" + " thread:" + Thread.currentThread().getName());
// 不會(huì)往下執(zhí)行observer的任何方法
observer.onCompleted();
System.out.println("complete after------" + " thread:" + Thread.currentThread().getName());
// 不會(huì)執(zhí)行到
observer.onCompleted();
// 不會(huì)執(zhí)行到
observer.onNext("abc");
}
} catch (Exception e) {
observer.onError(e);
}
}
});
}
public static void main(String[] args) {
Observable<String> observable = new HelloWorldHystrixObservableCommand("test").observe();
observable.subscribe(new Subscriber<String>() {
public void onCompleted() {
System.out.println("completed");
}
public void onError(Throwable throwable) {
System.out.println("error-----------" + throwable);
}
public void onNext(String v) {
System.out.println("next------------" + v);
}
});
}
}
執(zhí)行結(jié)果
in construct! thread:main
in call of construct! thread:main
complete before------ thread:main
complete after------ thread:main
next------------Hello1 thread:main
next------------Hello2 thread:main
next------------test thread:main
completed