Android RxJava :圖文詳解 背壓策略
![944365-207a738cb165a2da.png](https://i.iter01.com/images/a09334bdef74cc0fbf0f09156e4804f050fb78a9583ce923c7143c3e8d479f50.png)
前言
-
Rxjava
,由於其基於事件流的鏈式呼叫、邏輯簡潔 & 使用簡單的特點,深受各大Android
開發者的歡迎。
![944365-d0d78b3b826170ed.png](https://i.iter01.com/images/ee580d8436e3f85aa3a09a980179a6f34dbebe5c755393021b6b2d6dc6db418b.png)
如果還不瞭解RxJava,請看文章:Android:這是一篇 清晰 & 易懂的Rxjava 入門教程
- 本文主要講解的是
RxJava
中的 背壓控制策略,希望你們會喜歡。
- 本系列文章主要基於
Rxjava 2.0
- 接下來的時間,我將持續推出
Android
中Rxjava 2.0
的一系列文章,包括原理、操作符、應用場景、背壓等等 ,有興趣可以繼續關注Carson_Ho的安卓開發筆記!!
![944365-4c1c1eb44ffe01e5.png](https://i.iter01.com/images/bab48f9689d7513e05d3e4e9c85b740c4f80ba45a218cb544771eec4b5eb9016.png)
本文所有程式碼
Demo
均存放在Carson_Ho的Github地址
目錄
![944365-0ed59b40d7b73f7c.png](https://i.iter01.com/images/7d1fa64c15bf8812ad416da97604782c6e07a51592c9090b5edb313a4ceee4c8.png)
1. 引言
1.1 背景
- 觀察者 & 被觀察者 之間存在2種訂閱關係:同步 & 非同步。具體如下:
![944365-a8ca5dd7f71bd781.png](https://i.iter01.com/images/bb22e066a6efa458b7160283990dd8b1179ed0bdf7f34cf1e993329ebb64dbb2.png)
- 對於非同步訂閱關係,存在 被觀察者傳送事件速度 與觀察者接收事件速度 不匹配的情況
- 傳送 & 接收事件速度 = 單位時間內 傳送&接收事件的數量
- 大多數情況,主要是 被觀察者傳送事件速度 > 觀察者接收事件速度
1.2 問題
- 被觀察者 傳送事件速度太快,而觀察者 來不及接收所有事件,從而導致觀察者無法及時響應 / 處理所有傳送過來事件的問題,最終導致快取區溢位、事件丟失 & OOM
- 如,點選按鈕事件:連續過快的點選按鈕10次,則只會造成點選2次的效果;
- 解釋:因為點選速度太快了,所以按鈕來不及響應
下面再舉個例子:
- 被觀察者的傳送事件速度 = 10ms / 個
- 觀察者的接收事件速度 = 5s / 個
即出現傳送 & 接收事件嚴重不匹配的問題
Observable.create(new ObservableOnSubscribe<Integer>() {
// 1. 建立被觀察者 & 生產事件
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
for (int i = 0; ; i++) {
Log.d(TAG, "傳送了事件"+ i );
Thread.sleep(10);
// 傳送事件速度:10ms / 個
emitter.onNext(i);
}
}
}).subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Observer<Integer>() {
// 2. 通過通過訂閱(subscribe)連線觀察者和被觀察者
@Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "開始採用subscribe連線");
}
@Override
public void onNext(Integer value) {
try {
// 接收事件速度:5s / 個
Thread.sleep(5000);
Log.d(TAG, "接收到了事件"+ value );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "對Error事件作出響應");
}
@Override
public void onComplete() {
Log.d(TAG, "對Complete事件作出響應");
}
});
- 結果
由於被觀察者傳送事件速度 > 觀察者接收事件速度,所以出現流速不匹配問題,從而導致OOM
示意圖
1.3 解決方案
採用 背壓策略。
下面,我將開始介紹背壓策略。
2. 背壓策略簡介
2.1 定義
一種 控制事件流速 的策略
2.2 作用
在 非同步訂閱關係 中,控制事件傳送 & 接收的速度
注:背壓的作用域 = 非同步訂閱關係,即 被觀察者 & 觀察者處在不同執行緒中
2.3 解決的問題
解決了 因被觀察者傳送事件速度 與 觀察者接收事件速度 不匹配(一般是前者 快於 後者),從而導致觀察者無法及時響應 / 處理所有 被觀察者傳送事件 的問題
2.4 應用場景
- 被觀察者傳送事件速度 與 觀察者接收事件速度 不匹配的場景
- 具體場景就取決於 該事件的型別,如:網路請求,那麼具體場景:有很多網路請求需要執行,但執行者的執行速度沒那麼快,此時就需要使用背壓策略來進行控制。
3. 背壓策略的原理
- 那麼,RxJava實現背壓策略(
Backpressure
)的原理是什麼呢? - 解決方案 & 思想主要如下:
![944365-37ae2f5f93d9326c.png](https://i.iter01.com/images/5c187ae7c4614ea4520d8e3873a46cdc07ee1d07fd5f99494ec94548caf5aa79.png)
- 示意圖如下
![944365-d37b89b86aea104d.png](https://i.iter01.com/images/1ecfb4250ecbd41eebe2fd8bc9cdecff050ae96aecee54772e9bc9e310f09638.png)
- 與
RxJava1.0
中被觀察者的舊實現Observable
對比
![944365-c01363ed15386193.png](https://i.iter01.com/images/eb53d634bb3a87cb8e93b06bac3121b2a197c9c7fd4eef5cc73a7230b0357cbf.png)
- 好了,那麼上圖中在
RxJava 2.0
觀察者模型中,Flowable
到底是什麼呢?它其實是RxJava 2.0
中被觀察者的一種新實現,同時也是背壓策略實現的承載者 - 請繼續看下一節的介紹:背壓策略的具體實現 -
Flowable
4. 背壓策略的具體實現:Flowable
在 RxJava2.0
中,採用 Flowable
實現 背壓策略
正確來說,應該是 “非阻塞式背壓” 策略
4.1 Flowable 介紹
- 定義:在
RxJava2.0
中,被觀察者(Observable
)的一種新實現
同時,
RxJava1.0
中被觀察者(Observable
)的舊實現:Observable
依然保留
- 作用:實現 非阻塞式背壓 策略
4.2 Flowable 特點
-
Flowable
的特點 具體如下
![944365-ceca5a724ce25985.png](https://i.iter01.com/images/19c35e20beac6717a28f4da04ed6c1dbb47acfa3523e6aec1dc75aebabd8ea00.png)
- 下面再貼出一張
RxJava2.0
與RxJava1.0
的觀察者模型的對比圖
實際上,
RxJava2.0
也有保留(被觀察者)Observerble - Observer(觀察者)的觀察者模型,此處只是為了做出對比讓讀者瞭解
![944365-9c67239dfbc77eed.png](https://i.iter01.com/images/3af28bb0f34411db0d0d602e7d1bec7d75ed82909a7889ced7b200ba461f8f29.png)
4.3 與 RxJava1.0 中被觀察者的舊實現 Observable 的關係
- 具體如下圖
![944365-025e8828a7dd1fd9.png](https://i.iter01.com/images/b72be51a2a93eef4d5b46d8873e47d3fc7d0c624af4b97a73e9598aa8045cd36.png)
- 那麼,為什麼要採用新實現
Flowable
實現背壓,而不採用舊的Observable
呢? - 主要原因:舊實現
Observable
無法很好解決背壓問題。
![944365-2a2dac291a1ec818.png](https://i.iter01.com/images/1c1bafcdea50dc320bfe211dcd01cc11307f2ffdf002bff0767a9acc1a9892be.png)
4.4 Flowable的基礎使用
-
Flowable
的基礎使用非常類似於Observable
- 具體如下
/**
* 步驟1:建立被觀察者 = Flowable
*/
Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();
}
}, BackpressureStrategy.ERROR);
// 需要傳入背壓引數BackpressureStrategy,下面會詳細講解
/**
* 步驟2:建立觀察者 = Subscriber
*/
Subscriber<Integer> downstream = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
// 對比Observer傳入的Disposable引數,Subscriber此處傳入的引數 = Subscription
// 相同點:Subscription具備Disposable引數的作用,即Disposable.dispose()切斷連線, 同樣的呼叫Subscription.cancel()切斷連線
// 不同點:Subscription增加了void request(long n)
Log.d(TAG, "onSubscribe");
s.request(Long.MAX_VALUE);
// 關於request()下面會繼續詳細說明
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
};
/**
* 步驟3:建立訂閱關係
*/
upstream.subscribe(downstream);
![944365-dfd85d96ec5dc33c.png](https://i.iter01.com/images/1c8cc154350a06dc0123bc7ec30a44ec8e98324e5be9576e3b551bb5b7a91557.png)
- 更加優雅的鏈式呼叫
// 步驟1:建立被觀察者 = Flowable
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "傳送事件 1");
emitter.onNext(1);
Log.d(TAG, "傳送事件 2");
emitter.onNext(2);
Log.d(TAG, "傳送事件 3");
emitter.onNext(3);
Log.d(TAG, "傳送完成");
emitter.onComplete();
}
}, BackpressureStrategy.ERROR)
.subscribe(new Subscriber<Integer>() {
// 步驟2:建立觀察者 = Subscriber & 建立訂閱關係
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
s.request(3);
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
- 至此,
Flowable
的基礎使用講解完 - 關於更深層次的使用會結合 背壓策略的實現 來講解
5. 背壓策略的使用
- 在本節中,我將結合 背壓策略的原理 & Flowable的使用,為大家介紹在RxJava 2.0 中該如何使用Flowable來實現背壓策略功能,即背壓策略的使用
-
Flowable
與Observable
在功能上的區別主要是 多了背壓的功能 - 下面,我將順著第3節中講解背壓策略實現原理 & 解決方案(如下圖),來講解
Flowable
在背壓策略功能上的使用
![944365-e0ec10d25ccf64dd.png](https://i.iter01.com/images/5e870df2c25f24465ef0126e3083640624d5d6a79b8f460a8bd7f1a781680c3b.png)
注:
- 由於第2節中提到,使用背壓的場景 = 非同步訂閱關係,所以下文中講解的主要是非同步訂閱關係場景,即 被觀察者 & 觀察者 工作在不同執行緒中
- 但由於在同步訂閱關係的場景也可能出現流速不匹配的問題,所以在講解非同步情況後,會稍微講解一下同步情況,以方便對比
5.1 控制 觀察者接收事件 的速度
5.1.1 非同步訂閱情況
- 簡介
![944365-c016b71a080265b0.png](https://i.iter01.com/images/7b4714b54d125558ce7227af34f9b0ef244bd0c599750b0a2f6cd9ae4baf9389.png)
- 具體原理圖
![944365-3eebc18cf5bfe45b.png](https://i.iter01.com/images/a85bf7d5e003e1443794d99b07db876c622631f485df33b214ec72072bf3bbeb.png)
- 具體使用
// 1. 建立被觀察者Flowable
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 一共傳送4個事件
Log.d(TAG, "傳送事件 1");
emitter.onNext(1);
Log.d(TAG, "傳送事件 2");
emitter.onNext(2);
Log.d(TAG, "傳送事件 3");
emitter.onNext(3);
Log.d(TAG, "傳送事件 4");
emitter.onNext(4);
Log.d(TAG, "傳送完成");
emitter.onComplete();
}
}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
// 對比Observer傳入的Disposable引數,Subscriber此處傳入的引數 = Subscription
// 相同點:Subscription引數具備Disposable引數的作用,即Disposable.dispose()切斷連線, 同樣的呼叫Subscription.cancel()切斷連線
// 不同點:Subscription增加了void request(long n)
s.request(3);
// 作用:決定觀察者能夠接收多少個事件
// 如設定了s.request(3),這就說明觀察者能夠接收3個事件(多出的事件存放在快取區)
// 官方預設推薦使用Long.MAX_VALUE,即s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
- 效果圖
![944365-b4ac77634720e781.png](https://i.iter01.com/images/e673fea2cd5a4ae17faed264f7ce5b836e54c04bbf340373002b3017811fca9a.png)
- 有2個結論是需要大家注意的
![944365-bd5cdcab76667918.png](https://i.iter01.com/images/2ab9e099ab8251883fd6e8db6466a280c1752c48788f287398605bf7c8f7b77a.png)
下圖 = 當快取區存滿時(128個事件)溢位報錯的原理圖
![944365-30fdc84d48e39456.png](https://i.iter01.com/images/33e50854ab0039b1c8c54c32ecc764b2a3a8a48e673ea44dc286cf56176a9045.png)
- 程式碼演示1:觀察者不接收事件的情況下,被觀察者繼續傳送事件 & 存放到快取區;再按需取出
/**
* 步驟1:設定變數
*/
private static final String TAG = "Rxjava";
private Button btn; // 該按鈕用於呼叫Subscription.request(long n )
private Subscription mSubscription; // 用於儲存Subscription物件
/**
* 步驟2:設定點選事件 = 呼叫Subscription.request(long n )
*/
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mSubscription.request(2);
}
});
/**
* 步驟3:非同步呼叫
*/
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "傳送事件 1");
emitter.onNext(1);
Log.d(TAG, "傳送事件 2");
emitter.onNext(2);
Log.d(TAG, "傳送事件 3");
emitter.onNext(3);
Log.d(TAG, "傳送事件 4");
emitter.onNext(4);
Log.d(TAG, "傳送完成");
emitter.onComplete();
}
}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
mSubscription = s;
// 儲存Subscription物件,等待點選按鈕時(呼叫request(2))觀察者再接收事件
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-863d659e1f6f019c.gif](https://i.iter01.com/images/02041c352d7e4786a3c408b929c6ac103795127821b509608cf488db6ac0d285.gif)
- 程式碼演示2:觀察者不接收事件的情況下,被觀察者繼續傳送事件至超出快取區大小(128)
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 一共傳送129個事件,即超出了快取區的大小
for (int i = 0;i< 129; i++) {
Log.d(TAG, "傳送了事件" + i);
emitter.onNext(i);
}
emitter.onComplete();
}
}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
// 預設不設定可接收事件大小
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-f8f1703de76cb265.png](https://i.iter01.com/images/ad767a1d3c45cda946a4e8a4b228057fc135990c99902ee211671f8358b30ec9.png)
5.1.2 同步訂閱情況
同步訂閱 & 非同步訂閱 的區別在於:
- 同步訂閱中,被觀察者 & 觀察者工作於同1執行緒
- 同步訂閱關係中沒有快取區
![944365-1fe35f31544966ed.png](https://i.iter01.com/images/ad5b3b6142f6349ba7c84e26f6c0daa4230cefd6837bf18707f48d012e234970.png)
- 被觀察者在傳送1個事件後,必須等待觀察者接收後,才能繼續發下1個事件
/**
* 步驟1:建立被觀察者 = Flowable
*/
Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 傳送3個事件
Log.d(TAG, "傳送了事件1");
emitter.onNext(1);
Log.d(TAG, "傳送了事件2");
emitter.onNext(2);
Log.d(TAG, "傳送了事件3");
emitter.onNext(3);
emitter.onComplete();
}
}, BackpressureStrategy.ERROR);
/**
* 步驟2:建立觀察者 = Subscriber
*/
Subscriber<Integer> downstream = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
s.request(3);
// 每次可接收事件 = 3 二次匹配
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件 " + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
};
/**
* 步驟3:建立訂閱關係
*/
upstream.subscribe(downstream);
![944365-f62210c0e87994b3.png](https://i.iter01.com/images/343a9225c848eba538d68a50ab0e6bc10448216f0ea4d2ac0d47d9b005f0c785.png)
所以,實際上並不會出現被觀察者傳送事件速度 > 觀察者接收事件速度的情況。可是,卻會出現被觀察者傳送事件數量 > 觀察者接收事件數量的問題。
- 如:觀察者只能接受3個事件,但被觀察者卻傳送了4個事件,所以出現了不匹配情況
/**
* 步驟1:建立被觀察者 = Flowable
*/
Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 被觀察者傳送事件數量 = 4個
Log.d(TAG, "傳送了事件1");
emitter.onNext(1);
Log.d(TAG, "傳送了事件2");
emitter.onNext(2);
Log.d(TAG, "傳送了事件3");
emitter.onNext(3);
Log.d(TAG, "傳送了事件4");
emitter.onNext(4);
emitter.onComplete();
}
}, BackpressureStrategy.ERROR);
/**
* 步驟2:建立觀察者 = Subscriber
*/
Subscriber<Integer> downstream = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
s.request(3);
// 觀察者接收事件 = 3個 ,即不匹配
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件 " + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
};
/**
* 步驟3:建立訂閱關係
*/
upstream.subscribe(downstream);
![944365-b8f4f40ef2caaec3.png](https://i.iter01.com/images/f51cd851423538ab86c0e083d2bb33254eb7b4de3b8ba24bb552e4d94c245cb7.png)
所以,對於沒有快取區概念的同步訂閱關係來說,單純採用控制觀察者的接收事件數量(響應式拉取)實際上就等於 “單相思”,雖然觀察者控制了要接收3個事件,但假設被觀察者需要傳送4個事件,還是會出現問題。
在下面講解 5.2 控制被觀察者傳送事件速度 時會解決這個問題。
- 有1個特殊情況需要注意
![944365-deee1639cd045920.png](https://i.iter01.com/images/fce8d2be4f4913dd11201559a2ec6fabfbba2f379ce7ddeb98a5d96ae8955fce.png)
- 程式碼演示
/**
* 同步情況
*/
/**
* 步驟1:建立被觀察者 = Flowable
*/
Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "傳送了事件1");
emitter.onNext(1);
Log.d(TAG, "傳送了事件2");
emitter.onNext(2);
Log.d(TAG, "傳送了事件3");
emitter.onNext(3);
emitter.onComplete();
}
}, BackpressureStrategy.ERROR);
/**
* 步驟2:建立觀察者 = Subscriber
*/
Subscriber<Integer> downstream = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
// 不設定request(long n)
// s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
};
/**
* 步驟3:建立訂閱關係
*/
upstream.subscribe(downstream);
在被觀察者傳送第1個事件後, 就丟擲MissingBackpressureException
異常 & 觀察者沒有收到任何事件
![944365-29cebc2be381fbea.png](https://i.iter01.com/images/6c9bc9e6f4d077610bcaeb5b0898ca319ec3741f4afdfcd7409d5bff0cf0f6e8.png)
5.2 控制 被觀察者傳送事件 的速度
- 簡介
![944365-f18a40b597c68c8f.png](https://i.iter01.com/images/0e430ba4bce79bf093c78c2f44193c083e5abca3d1a863fd7853bb33c10a7aa2.png)
-
FlowableEmitter
類的requested()
介紹
public interface FlowableEmitter<T> extends Emitter<T> {
// FlowableEmitter = 1個介面,繼承自Emitter
// Emitter介面方法包括:onNext(),onComplete() & onError
long requested();
// 作用:返回當前執行緒中request(a)中的a值
// 該request(a)則是措施1中講解的方法,作用 = 設定
....// 僅貼出關鍵程式碼
}
每個執行緒中的
requested()
的返回值 = 該執行緒中的request(a)
的a值對應於同步 & 非同步訂閱情況 的原理圖
![944365-88e1f3c641eb54e3.png](https://i.iter01.com/images/30d976f2876e839170d78a18be56bc8bd040b874c06fad831199e6905fb234f7.png)
為了方便大家理解該策略中的requested()
使用,該節會先講解同步訂閱情況,再講解非同步訂閱情況
5.2.1 同步訂閱情況
- 原理說明
![944365-233a841e920c83e0.png](https://i.iter01.com/images/b7003ffb59a5c13cdc904313b1c52bd447423a9c2381ff225abf780d8c724f7b.png)
即在同步訂閱情況中,被觀察者 通過 FlowableEmitter.requested()
獲得了觀察者自身接收事件能力,從而根據該資訊控制事件傳送速度,從而達到了觀察者反向控制被觀察者的效果
- 具體使用
下面的例子 = 被觀察者根據觀察者自身接收事件能力(10個事件),從而僅傳送10個事件
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 呼叫emitter.requested()獲取當前觀察者需要接收的事件數量
long n = emitter.requested();
Log.d(TAG, "觀察者可接收事件" + n);
// 根據emitter.requested()的值,即當前觀察者需要接收的事件數量來傳送事件
for (int i = 0; i < n; i++) {
Log.d(TAG, "傳送了事件" + i);
emitter.onNext(i);
}
}
}, BackpressureStrategy.ERROR)
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
// 設定觀察者每次能接受10個事件
s.request(10);
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-9c66a73f6bc4d108.png](https://i.iter01.com/images/6fb17172dd8388b3048cce68b1fb8625906cc827fe781c938f118ab94e199ed6.png)
- 特別注意
在同步訂閱情況中使用FlowableEmitter.requested()
時,有以下幾種使用特性需要注意的:
![944365-bf897a2c6d664bbb.png](https://i.iter01.com/images/4f70a0aa884c63f93fe02cdc95dbc119849fe86994610454cbffa24d129b7a28.png)
情況1:可疊加性
- 即:觀察者可連續要求接收事件,被觀察者會進行疊加並一起傳送
Subscription.request(a1);
Subscription.request(a2);
FlowableEmitter.requested()的返回值 = a1 + a2
- 程式碼演示
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 呼叫emitter.requested()獲取當前觀察者需要接收的事件數量
Log.d(TAG, "觀察者可接收事件" + emitter.requested());
}
}, BackpressureStrategy.ERROR)
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
s.request(10); // 第1次設定觀察者每次能接受10個事件
s.request(20); // 第2次設定觀察者每次能接受20個事件
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-d9fa2818e086fc9f.png](https://i.iter01.com/images/188b849b81ac42c25d5a39c3d8396b5e1d1eb11d2e1c684144bb497bec0fc4ef.png)
情況2:實時更新性
- 即,每次傳送事件後,emitter.requested()會實時更新觀察者能接受的事件
- 即一開始觀察者要接收10個事件,傳送了1個後,會實時更新為9個
- 僅計算
Next
事件,complete & error
事件不算。
Subscription.request(10);
// FlowableEmitter.requested()的返回值 = 10
FlowableEmitter.onNext(1); // 傳送了1個事件
// FlowableEmitter.requested()的返回值 = 9
- 程式碼演示
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 1. 呼叫emitter.requested()獲取當前觀察者需要接收的事件數量
Log.d(TAG, "觀察者可接收事件數量 = " + emitter.requested());
// 2. 每次傳送事件後,emitter.requested()會實時更新觀察者能接受的事件
// 即一開始觀察者要接收10個事件,傳送了1個後,會實時更新為9個
Log.d(TAG, "傳送了事件 1");
emitter.onNext(1);
Log.d(TAG, "傳送了事件1後, 還需要傳送事件數量 = " + emitter.requested());
Log.d(TAG, "傳送了事件 2");
emitter.onNext(2);
Log.d(TAG, "傳送事件2後, 還需要傳送事件數量 = " + emitter.requested());
Log.d(TAG, "傳送了事件 3");
emitter.onNext(3);
Log.d(TAG, "傳送事件3後, 還需要傳送事件數量 = " + emitter.requested());
emitter.onComplete();
}
}, BackpressureStrategy.ERROR)
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
s.request(10); // 設定觀察者每次能接受10個事件
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-fd91525258e3feeb.png](https://i.iter01.com/images/d0ee4bd8ada0804f52c6dcb1b1d75bb82f9ec9bef895f65c8e8f1c450d6aad4f.png)
情況3:異常
- 當
FlowableEmitter.requested()
減到0時,則代表觀察者已經不可接收事件 - 此時被觀察者若繼續傳送事件,則會丟擲
MissingBackpressureException
異常
如觀察者可接收事件數量 = 1,當被觀察者傳送第2個事件時,就會丟擲異常
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 1. 呼叫emitter.requested()獲取當前觀察者需要接收的事件數量
Log.d(TAG, "觀察者可接收事件數量 = " + emitter.requested());
// 2. 每次傳送事件後,emitter.requested()會實時更新觀察者能接受的事件
// 即一開始觀察者要接收10個事件,傳送了1個後,會實時更新為9個
Log.d(TAG, "傳送了事件 1");
emitter.onNext(1);
Log.d(TAG, "傳送了事件1後, 還需要傳送事件數量 = " + emitter.requested());
Log.d(TAG, "傳送了事件 2");
emitter.onNext(2);
Log.d(TAG, "傳送事件2後, 還需要傳送事件數量 = " + emitter.requested());
emitter.onComplete();
}
}, BackpressureStrategy.ERROR)
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
s.request(1); // 設定觀察者每次能接受1個事件
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-acbf17d1cf200c0c.png](https://i.iter01.com/images/89862f2da76941d578d8c5f2974ad5c31aa06e6aeb58659fd40728cfe229d397.png)
額外
- 若觀察者沒有設定可接收事件數量,即無呼叫
Subscription.request()
- 那麼被觀察者預設觀察者可接收事件數量 = 0,即
FlowableEmitter.requested()
的返回值 = 0
5.2.2 非同步訂閱情況
- 原理說明
![944365-45e520e7016cf9fd.png](https://i.iter01.com/images/6e123455f87703372f4c081a84d7c55cc4fba640f2f45be197419d6643bc6a5a.png)
從上面可以看出,由於二者處於不同執行緒,所以被觀察者 無法通過 FlowableEmitter.requested()
知道觀察者自身接收事件能力,即 被觀察者不能根據 觀察者自身接收事件的能力 控制傳送事件的速度。具體請看下面例子
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 呼叫emitter.requested()獲取當前觀察者需要接收的事件數量
Log.d(TAG, "觀察者可接收事件數量 = " + emitter.requested());
}
}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
s.request(150);
// 該設定僅影響觀察者執行緒中的requested,卻不會影響的被觀察者中的FlowableEmitter.requested()的返回值
// 因為FlowableEmitter.requested()的返回值 取決於RxJava內部呼叫request(n),而該內部呼叫會在一開始就呼叫request(128)
// 為什麼是呼叫request(128)下面再講解
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-1d94a2a6a7c510f2.png](https://i.iter01.com/images/e0579230f8d6c6718363d5d79ae485fb6e80bf69a28d37187a5fc3f461967c49.png)
而在非同步訂閱關係中,反向控制的原理是:通過RxJava
內部固定呼叫被觀察者執行緒中的request(n)
從而 反向控制被觀察者的傳送事件速度
那麼該什麼時候呼叫被觀察者執行緒中的request(n)
& n
的值該是多少呢?請繼續往下看。
- 具體使用
關於RxJava
內部呼叫request(n)(n = 128、96、0)
的邏輯如下:
![944365-f6314aba60c08455.png](https://i.iter01.com/images/da777cc3ffd8d8d8400cb43f663db1c2f558c915c41c1b6a1acce7f0ea6bed04.png)
至於為什麼是呼叫
request(128)
&request(96)
&request(0)
,感興趣的讀者可自己閱讀Flowable
的原始碼
- 程式碼演示
下面我將用一個例子來演示該原理的邏輯
// 被觀察者:一共需要傳送500個事件,但真正開始傳送事件的前提 = FlowableEmitter.requested()返回值 ≠ 0
// 觀察者:每次接收事件數量 = 48(點選按鈕)
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
Log.d(TAG, "觀察者可接收事件數量 = " + emitter.requested());
boolean flag; //設定標記位控制
// 被觀察者一共需要傳送500個事件
for (int i = 0; i < 500; i++) {
flag = false;
// 若requested() == 0則不傳送
while (emitter.requested() == 0) {
if (!flag) {
Log.d(TAG, "不再傳送");
flag = true;
}
}
// requested() ≠ 0 才傳送
Log.d(TAG, "傳送了事件" + i + ",觀察者可接收事件數量 = " + emitter.requested());
emitter.onNext(i);
}
}
}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
mSubscription = s;
// 初始狀態 = 不接收事件;通過點選按鈕接收事件
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
// 點選按鈕才會接收事件 = 48 / 次
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mSubscription.request(48);
// 點選按鈕 則 接收48個事件
}
});
整個流程 & 測試結果 請看下圖
![944365-c3c362cd8e101867.png](https://i.iter01.com/images/9b83c4e40abdc7d159784d2842e9ae74227dfa936f7758eda8816f7726ad0c3c.png)
5.3 採用背壓策略模式:BackpressureStrategy
5.3.1 背壓模式介紹
在Flowable的使用中,會被要求傳入背壓模式引數
![944365-ccd84761c65ddaa6.png](https://i.iter01.com/images/356a8c674c54a0d0e9c586204455f9a3d2885897356b4a856d2edcaa53fd7dee.png)
- 物件導向:針對快取區
- 作用:當快取區大小存滿、被觀察者仍然繼續傳送下1個事件時,該如何處理的策略方式
快取區大小存滿、溢位 = 傳送事件速度 > 接收事件速度 的結果 = 傳送 & 接收事件不匹配的結果
5.3.2 背壓模式型別
![944365-47b55edec299faea.png](https://i.iter01.com/images/53173529b01765b236437bfaff49c9b44330eb2426230302166bb44ed491137a.png)
下面我將對每種模式逐一說明。
模式1:BackpressureStrategy.ERROR
- 問題:傳送事件速度 > 接收事件 速度,即流速不匹配
具體表現:出現當快取區大小存滿(預設快取區大小 = 128)、被觀察者仍然繼續傳送下1個事件時
- 處理方式:直接丟擲異常
MissingBackpressureException
// 建立被觀察者Flowable
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 傳送 129個事件
for (int i = 0;i< 129; i++) {
Log.d(TAG, "傳送了事件" + i);
emitter.onNext(i);
}
emitter.onComplete();
}
}, BackpressureStrategy.ERROR) // 設定背壓模式 = BackpressureStrategy.ERROR
.subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-0c56eb0868106c41.png](https://i.iter01.com/images/2f3f322e966fa88a9e92d14a93709112de8ad9d24ebbd89e1ad093bd1bce130d.png)
模式2:BackpressureStrategy.MISSING
- 問題:傳送事件速度 > 接收事件 速度,即流速不匹配
具體表現是:出現當快取區大小存滿(預設快取區大小 = 128)、被觀察者仍然繼續傳送下1個事件時
- 處理方式:友好提示:快取區滿了
// 建立被觀察者Flowable
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 傳送 129個事件
for (int i = 0;i< 129; i++) {
Log.d(TAG, "傳送了事件" + i);
emitter.onNext(i);
}
emitter.onComplete();
}
}, BackpressureStrategy.MISSING) // 設定背壓模式 = BackpressureStrategy.MISSING
.subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-8f27f7fe6258bea6.png](https://i.iter01.com/images/c93d4f12b0b213941a6d8657563da9be0fdf84b6ecf12bc0e0cf9a8b4c4d9fea.png)
模式3:BackpressureStrategy.BUFFER
- 問題:傳送事件速度 > 接收事件 速度,即流速不匹配
具體表現是:出現當快取區大小存滿(預設快取區大小 = 128)、被觀察者仍然繼續傳送下1個事件時
- 處理方式:將快取區大小設定成無限大
- 即 被觀察者可無限傳送事件 觀察者,但實際上是存放在快取區
- 但要注意記憶體情況,防止出現OOM
// 建立被觀察者Flowable
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 傳送 129個事件
for (int i = 1;i< 130; i++) {
Log.d(TAG, "傳送了事件" + i);
emitter.onNext(i);
}
emitter.onComplete();
}
}, BackpressureStrategy.BUFFER) // 設定背壓模式 = BackpressureStrategy.BUFFER
.subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
可以接收超過原先快取區大小(128)的事件數量了
![944365-f1fffce5c7925567.png](https://i.iter01.com/images/1d875e82b65e4a4927282a624c5895a488c5e4086e54803b25ec45d6239c0d95.png)
模式4: BackpressureStrategy.DROP
- 問題:傳送事件速度 > 接收事件 速度,即流速不匹配
具體表現是:出現當快取區大小存滿(預設快取區大小 = 128)、被觀察者仍然繼續傳送下1個事件時
- 處理方式:超過快取區大小(128)的事件丟棄
如傳送了150個事件,僅儲存第1 - 第128個事件,第129 -第150事件將被丟棄
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
// 傳送150個事件
for (int i = 0;i< 150; i++) {
Log.d(TAG, "傳送了事件" + i);
emitter.onNext(i);
}
emitter.onComplete();
}
}, BackpressureStrategy.DROP) // 設定背壓模式 = BackpressureStrategy.DROP
.subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
mSubscription = s;
// 通過按鈕進行接收事件
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mSubscription.request(128);
// 每次接收128個事件
}
});
被觀察者一下子傳送了150個事件,點選按鈕接收時觀察者接收了128個事件;再次點選接收時卻無法接受事件,這說明超過快取區大小的事件被丟棄了。
![944365-6b601cfdcaa0eb29.gif](https://i.iter01.com/images/8fe009307900d97476890fceae4204c5981039f1cf7084a63f15eb0d5188ac98.gif)
模式5:BackpressureStrategy.LATEST
- 問題:傳送事件速度 > 接收事件 速度,即流速不匹配
具體表現是:出現當快取區大小存滿(預設快取區大小 = 128)、被觀察者仍然繼續傳送下1個事件時
- 處理方式:只儲存最新(最後)事件,超過快取區大小(128)的事件丟棄
即如果傳送了150個事件,快取區裡會儲存129個事件(第1-第128 + 第150事件)
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
for (int i = 0;i< 150; i++) {
Log.d(TAG, "傳送了事件" + i);
emitter.onNext(i);
}
emitter.onComplete();
}
}, BackpressureStrategy.LATEST) // // 設定背壓模式 = BackpressureStrategy.LATEST
.subscribeOn(Schedulers.io()) // 設定被觀察者在io執行緒中進行
.observeOn(AndroidSchedulers.mainThread()) // 設定觀察者在主執行緒中進行
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
mSubscription = s;
// 通過按鈕進行接收事件
}
@Override
public void onNext(Integer integer) {
Log.d(TAG, "接收到了事件" + integer);
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mSubscription.request(128);
// 每次接收128個事件
}
});
- 被觀察者一下子傳送了150個事件,點選按鈕接收時觀察者接收了128個事件;
- 再次點選接收時卻接收到1個事件(第150個事件),這說明超過快取區大小的事件僅保留最後的事件(第150個事件)
![944365-006167898961a15b.gif](https://i.iter01.com/images/c6b0fee628b74bff36b34d8df7c9d38fb5a101c72b412474436641aa7458aaab.gif)
5.3.3 特別注意
在使用背壓策略模式的時候,有1種情況是需要注意的:
a. 背景FLowable
可通過自己建立(如上面例子),或通過其他方式自動建立,如interval操作符
interval操作符簡介
- 作用:每隔1段時間就產生1個數字(Long型),從0開始、1次遞增1,直至無窮大
- 預設執行在1個新執行緒上
- 與timer操作符區別:timer操作符可結束髮送
b. 衝突
對於自身手動建立
FLowable
的情況,可通過傳入背壓模式引數選擇背壓策略
(即上面描述的)可是對於自動建立
FLowable
,卻無法手動傳入傳入背壓模式引數,那麼出現流速不匹配的情況下,該如何選擇 背壓模式呢?
// 通過interval自動建立被觀察者Flowable
// 每隔1ms將當前數字(從0開始)加1,併傳送出去
// interval操作符會預設新開1個新的工作執行緒
Flowable.interval(1, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.newThread()) // 觀察者同樣工作在一個新開執行緒中
.subscribe(new Subscriber<Long>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
mSubscription = s;
s.request(Long.MAX_VALUE); //預設可以接收Long.MAX_VALUE個事件
}
@Override
public void onNext(Long aLong) {
Log.d(TAG, "onNext: " + aLong);
try {
Thread.sleep(1000);
// 每次延時1秒再接收事件
// 因為傳送事件 = 延時1ms,接收事件 = 延時1s,出現了傳送速度 & 接收速度不匹配的問題
// 快取區很快就存滿了128個事件,從而丟擲MissingBackpressureException異常,請看下圖結果
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
![944365-ff404e17814378eb.png](https://i.iter01.com/images/8472a28e214ce06a4ac642ef3955a406abb16d0b371b181b85dfd380f914d322.png)
c. 解決方案RxJava 2.0
內部提供 封裝了背壓策略模式的方法
onBackpressureBuffer()
onBackpressureDrop()
onBackpressureLatest()
預設採用
BackpressureStrategy.ERROR
模式
具體使用如下:
Flowable.interval(1, TimeUnit.MILLISECONDS)
.onBackpressureBuffer() // 新增背壓策略封裝好的方法,此處選擇Buffer模式,即快取區大小無限制
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<Long>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
mSubscription = s;
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Long aLong) {
Log.d(TAG, "onNext: " + aLong);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
從而很好地解決了傳送事件 & 接收事件 速度不匹配的問題。
![944365-814e7dcd89ae95b7.gif](https://i.iter01.com/images/a3e4e1730c9e71fc05d525e14cb9286efd66c23cc96025d543eaf818dd70402a.gif)
其餘方法的作用類似於上面的說背壓模式引數,此處不作過多描述。
背壓策略模式小結
![944365-1be234a3c566c7bc.png](https://i.iter01.com/images/1b722bed3405ff0432a878a3b45d0272d10dfaad1fcffd4b5de7152153080fb6.png)
- 至此,對
RxJava 2.0
的背壓模式終於講解完畢 - 所有程式碼Demo均存放在Carson_Ho的Github地址
6. 總結
本文主要對
Rxjava
的背壓模式知識進行講解接下來的時間,我將持續推出
Android
中Rxjava 2.0
的一系列文章,包括原理、操作符、應用場景、背壓等等 ,有興趣可以繼續關注Carson_Ho的安卓開發筆記!!
![944365-4c1c1eb44ffe01e5.png](https://i.iter01.com/images/bab48f9689d7513e05d3e4e9c85b740c4f80ba45a218cb544771eec4b5eb9016.png)
請點贊!因為你的鼓勵是我寫作的最大動力!
相關文章閱讀
- 操作符使用
Android:這是一篇 清晰 & 易懂的Rxjava 入門教程
Android RxJava:最基礎的操作符詳解 - 建立操作符
Android RxJava:圖文詳解 變換操作符
Android RxJava:組合 / 合併操作符 詳細教程
Android RxJava:功能性操作符 全面講解- 實際應用講解
Android RxJava 實際應用講解:(無條件)網路請求輪詢
Android RxJava 實際應用講解:(有條件)網路請求輪詢
Android RxJava 實際應用講解:網路請求巢狀回撥
Android RxJava 實際應用講解:合併資料來源
Android RxJava 實際應用講解:從磁碟 / 記憶體快取中 獲取快取資料
Android RxJava 實際應用講解:聯合判斷
Android RxJava:細說 執行緒控制(切換 / 排程 )(含Retrofit例項講解)
Android RxJava 實際應用講解:網路請求出錯重連(結合Retrofit)
歡迎關注Carson_Ho的簡書!
不定期分享關於安卓開發的乾貨,追求短、平、快,但卻不缺深度。
![944365-9b76fa3c52d478a7.png](https://i.iter01.com/images/fadf4c8c2b6950404da173c6f66a5e9c8b5839ce9bf1226015258931f5dc27b4.png)
相關文章
- 5章 RxJava背壓策略RxJava
- Android Rxjava :最簡單&全面背壓講解 (Flowable)AndroidRxJava
- RxJava2系列之背壓策略(一)RxJava
- RxJava 系列-2:背壓和 FlowableRxJava
- part06_Rxjava背壓原理RxJava
- RxJava2 系列-2:背壓和FlowableRxJava
- Android Rxjava:圖解不一樣的詮釋AndroidRxJava圖解
- Android 向量圖詳解Android
- 一文詳解AI模型部署策略AI模型
- Rxjava2(二)、五種觀察者模式建立及背壓RxJava模式
- Java泛型詳解,史上最全圖文詳解!Java泛型
- Android Bitmap(點陣圖)詳解Android
- PopClip使用教程圖文詳解
- [譯]RxJava 的全面介紹:Observable 型別、背壓、錯誤處理RxJava型別
- Android——RxJava2史上最全講解AndroidRxJava
- 分頁機制圖文詳解
- 同源策略詳解
- CentOS 7安裝教程(圖文詳解)CentOS
- 圖文詳解23種設計模式設計模式
- 圖文並茂詳解 NAT 協議!協議
- Jenkins安裝部署使用圖文詳解(非常詳細)Jenkins
- 科學上網-搬瓦工 VPS 自己搭建 ss 圖文詳解(mac/windows/android/ios)MacWindowsAndroidiOS
- RxJava2 錯誤處理詳解RxJava
- EventBus 3.0+ 原始碼詳解(史上最詳細圖文講解)原始碼
- HDFS balance策略詳解
- Linux 策略路由詳解Linux路由
- Android 接入騰訊IM即時通訊(詳細圖文)Android
- Android圖片載入框架Fresco使用詳解Android框架
- 事務隔離級別(圖文詳解)
- ZooKeeper最全詳解(萬字圖文總結)
- 微服務最全詳解(圖文全面總結)微服務
- 常用負載均衡詳解(圖文總結)負載
- Wgpu圖文詳解(03)緩衝區BufferGPU
- LL圖文詳解mysql中with...as用法huxMySqlUX
- cookie與session的區別(圖文詳解)CookieSession
- Android系統資源管理與電池最佳化策略詳解Android
- Linux下的tar壓縮解壓縮命令詳解Linux
- Linux 常用的壓縮與解壓縮命令詳解Linux
- 壓縮命令tar詳解