RxJava 從入門到全解析
前言
使用了RxJava有一段時間了,深深感受到了其“牛逼”之處。下面,就從RxJava的基礎開始,一步一步與大家分享一下這個強大的非同步庫的用法!
上給的解釋是:“RxJava is a Java VM implementation of Reactive Extensions:
a library for composing asynchronous and event-based programs by using observable sequences.”
大概就是說RxJava是Java VM上一個靈活的、使用可觀測序列來組成的一個非同步的、基於事件的庫。咋一看好像不知道是啥東西… … 沒事,往下看~
這段解釋,重點就在於非同步
!但是它又不像 AsyncTask
這樣用法簡單,所以剛接觸RxJava的童鞋,可能會覺得特別難,無從下手,沒事,相信透過這篇文章,大夥兒可以有一個比較深刻的理解!
RxJava精華可以濃縮為非同步
兩個字,其核心的東西不外乎兩個:
1. Observable(被觀察者) 2. Observer/Subscriber(觀察者)
Observables可以發出一系列的 事件
,這裡的事件可以是任何東西,例如網路請求、複雜計算處理、資料庫操作、檔案操作等等,事件執行結束後交給 Observer/Subscriber 的回撥處理。
下獲取最新版本。
後面會詳細介紹,這裡先有個瞭解。// 建立物件,just裡面的每一個引數,相當於呼叫一次Subscriber#OnNext()Observable observable = Observable.just("Hello World!");
// 建立物件,just裡面的每一個引數,相當於呼叫一次Subscriber#OnNext()Observableobservable = Observable.just("Hello World!");
這樣,是不是簡單了許多?
進行消費。首先,先建立一個觀察者。// 建立一個ObserverObserver observer = new Observer() { @Override
public void onCompleted() {
Log.i(TAG, "complete");
} @Override
public void onError(Throwable e) {
} @Override
public void onNext(String s) {
Log.i(TAG, s);
}
};
或者
// 建立一個SubscriberSubscribersubscriber = new Subscriber () { @Override public void onCompleted() { Log.i(TAG, "complete"); } @Override public void onError(Throwable e) { } @Override public void onNext(String s) { Log.i(TAG, s); } };
Observer 是觀察者, Subscriber 也是觀察者,Subscriber 是一個實現了Observer介面的抽象類,對 Observer 進行了部分擴充套件,在使用上基本沒有區別;
Subscriber 多了傳送之前呼叫的
onStart()
和解除訂閱關係的unsubscribe()
方法。並且,在 RxJava 的 subscribe 過程中,Observer 也總是會先被轉換成一個 Subscriber 再使用。所以在這之後的示例程式碼,都使用 Subscriber 來作為觀察者。
與來說,好像為了列印一個“Hello World!”要費好大的勁… 其實,RxJava 自身提供了精簡回撥方式,我們可以為 Subscriber 中的三種狀態根據自身需要分別建立一個回撥動作 Action
:// onComplete()Action0 onCompleteAction = new Action0() { @Override
public void call() {
Log.i(TAG, "complete");
}
};// onNext(T t)Action1 onNextAction = new Action1() { @Override
public void call(String s) {
Log.i(TAG, s);
}
};// onError(Throwable t)Action1 onErrorAction = new Action1() { @Override
public void call(Throwable throwable) {
}
};
那麼,RxJava 的事件訂閱支援以下三種不完整定義的回撥。
observable.subscribe(onNextAction); observable.subscribe(onNextAction, onErrorAction); observable.subscribe(onNextAction, onErrorAction, onCompleteAction);
我們可以根據當前需要,傳入對應的 Action, RxJava 會相應的自動建立 Subscriber。
Action0 表示一個無回撥引數的Action;
Action1 表示一個含有一個回撥引數的Action;
當然,還有Action2 ~ Action9,分別對應2~9個引數的Action;
每個Action,都有一個 call() 方法,透過泛型T,來指定對應引數的型別;
進行改寫。final ImageView ivLogo = (ImageView) findViewById(R.id.ivLogo);
Observable.create(new Observable.OnSubscribe() { @Override
public void call(Subscriber super String> subscriber) {
subscriber.onNext("https://ss2.baidu.com/-vo3dSag_xI4khGko9WTAnF6hhy/image/h%3D200/sign=4db5130a073b5bb5a1d727fe06d2d523/cf1b9d16fdfaaf51965f931e885494eef11f7ad6.jpg");
}
}).map(new Func1() { @Override
public Drawable call(String url) { try {
Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), "src"); return drawable;
} catch (IOException e) {
} return null;
}
}) // 指定 subscribe() 所在的執行緒,也就是call()方法呼叫的執行緒
.subscribeOn(Schedulers.io()) // 指定 Subscriber 回撥方法所在的執行緒,也就是onCompleted, onError, onNext回撥的執行緒
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() { @Override
public void onCompleted() {
} @Override
public void onError(Throwable e) {
Log.e(TAG, e.toString());
} @Override
public void onNext(Drawable drawable) { if (drawable != null) {
ivLogo.setImageDrawable(drawable);
}
}
});
經過改寫程式碼後,有什麼變化呢? Observable 建立了一個 String 事件,也就是產生一個url,透過 map
運算子進行變換,返回Drawable物件,這個變換指的就是透過url進行網路圖片請求,返回一個Drawable。所以簡單的來說就是把String事件,轉換為Drawable事件。邏輯表示就是:
Observable--> map變換 --> Observable
那麼,Func1
是什麼呢?與 Action1
類似,不同的是 FuncX
有返回值,而 ActionX
沒有。為什麼需要返回值呢?目的就在於物件的變換,由String物件轉換為Drawable物件。同樣,也有Func0 ~ Func9,對應不同的引數個數。
當然了,RxJava 的變換,可不止於map這麼簡單,繼續往下!
會介紹這個。為了演示效果,先舉個簡單栗子:Observable
.create(new Observable.OnSubscribe() { @Override
public void call(Subscriber super Integer> subscriber) { int i = 0; int[] times = new int[]{100, 1000}; while (true) {
i++; if (i >= 100) break;
subscriber.onNext(i); try { // 注意!!!!
// 當i為奇數時,休眠1000ms,然後才傳送i+1,這時i不會被過濾掉
// 當i為偶數時,只休眠100ms,便傳送i+1,這時i會被過濾掉
Thread.sleep(times[i % 2]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
subscriber.onCompleted();
}
}) // 間隔400ms以內的事件將被丟棄
.debounce(400, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() { @Override
public void onCompleted() {
Log.i(TAG, "complete");
} @Override
public void onError(Throwable e) {
Log.e(TAG, e.toString());
} @Override
public void onNext(Integer integer) {
Log.i(TAG, "integer = " + integer);
}
});
輸出結果:
11-23 10:44:45.167 MainActivity: integer = 111-23 10:44:46.270 MainActivity: integer = 311-23 10:44:47.373 MainActivity: integer = 511-23 10:44:48.470 MainActivity: integer = 711-23 10:44:49.570 MainActivity: integer = 911-23 10:44:50.671 MainActivity: integer = 1111-23 10:44:51.772 MainActivity: integer = 1311-23 10:44:52.872 MainActivity: integer = 1511-23 10:44:53.973 MainActivity: integer = 17...
我們設定過濾條件為400ms,可以發現,奇數正常輸出,因為在它的下一個事件事件隔了1000ms,所以它不會被過濾掉;偶數被過濾掉,是因為它距離下一個事件(奇數)只隔了100ms。並且,輸出的兩個事件相隔大約為 100ms + 1000ms = 1100ms
。
merge
用於合併兩個Observable為一個Observable。較為簡單。
Observable.merge(Observable1, Observable2) .subscribe(subscriber);
concat
順序執行多個Observable,個數為1 ~ 9。例子稍後與first運算子一起~~
compose
與 flatMap
類似,都是進行變換,返回Observable物件,啟用併傳送事件。
compose
是唯一一個能夠從資料流中得到原始Observable的運算子,所以,那些需要對整個資料流產生作用的操作(比如,subscribeOn()和observeOn())需要使用compose
來實現。相較而言,如果在flatMap()中使用subscribeOn()或者observeOn(),那麼它僅僅對在flatMap
中建立的Observable起作用,而不會對剩下的流產生影響。這樣就可以簡化subscribeOn()以及observeOn()的呼叫次數了。compose
是對 Observable 整體的變換,換句話說,flatMap
轉換Observable裡的每一個事件,而compose
轉換的是整個Observable資料流。flatMap
每傳送一個事件都建立一個 Observable,所以效率較低。而compose
運算子只在主幹資料流上執行操作。建議使用
compose
代替flatMap
。
first
只傳送符合條件的第一個事件。可以與前面的contact運算子,做網路快取。舉個例子:依次檢查Disk與Network,如果Disk存在快取,則不做網路請求,否則進行網路請求。
// 從快取獲取ObservablefromDisk = Observable.create(new Observable.OnSubscribe () { @Override public void call(Subscriber super BookList> subscriber) { BookList list = getFromDisk(); if (list != null) { subscriber.onNext(list); } else { subscriber.onCompleted(); } } });// 從網路獲取Observable fromNetWork = bookApi.getBookDetailDisscussionList(); Observable.concat(fromDisk, fromNetWork) // 如果快取不為null,則不再進行網路請求。反之 .first() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber () { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(BookList discussionList) { } });
網路快取用法,具體可參見我的專案:
timer
可以做定時操作,換句話講,就是延遲執行。事件間隔由timer控制。舉個例子:兩秒後輸出“Hello World!”
Observable.timer(2, TimeUnit.SECONDS) .subscribe(new Subscriber() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Long aLong) { Log.i(TAG, "Hello World!"); } });
interval
定時的週期性操作,與timer的區別就在於它可以重複操作。事件間隔由interval控制。舉個例子:每隔兩秒輸出“Hello World!”
Observable.interval(2, TimeUnit.SECONDS) .subscribe(new Subscriber() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Long aLong) { Log.i(TAG, "Hello World!"); } });
throttleFirst
與debounce類似,也是時間間隔太短,就丟棄事件。可以用於防抖操作,比如防止雙擊。
RxView.clicks(button) .throttleFirst(1, TimeUnit.SECONDS) .subscribe(new Observer
上面這個RxView詳見:, 主要與RxJava結合用於一些View的事件繫結,JakeWharton大神的專案,厲害。
Single
Single與Observable類似,相當於是他的精簡版。訂閱者回撥的不是OnNext/OnError/onCompleted,而是回撥OnSuccess/OnError。
Single.create(new Single.OnSubscribe
Subject
Subject這個類,既是Observable又是Observer,啥意思呢?就是它自身既是事件的生產者,又是事件的消費者,相當於自身是一條管道,從一端進,又從另一端出。舉個例子:PublishSubject
Subject subject = PublishSubject.create();// 1.由於Subject是Observable,所以進行訂閱subject.subscribe(new Subscriber() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Object o) { Log.i(TAG, o.toString()); } });// 2.由於Subject同時也是Observer,所以可以呼叫onNext傳送資料subject.onNext("world");
這個好像有點厲害的樣子,哈哈。可以配合debounce,避免SearchEditText頻繁請求。
Subject subject = PublishSubject.create(); subject.debounce(400, TimeUnit.MILLISECONDS) .subscribe(new Subscriber() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Object o) { // request } }); edittext.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { subject.onNext(s.toString()); } @Override public void afterTextChanged(Editable s) { } });
最後的獲取Readme內容顯示在WebView上的例子。Retrofit2 + RxJava + Dagger2: 具體可參見我的專案,裡面有比較詳細的用法。
Retrofit2 + RxJava + Dagger2: 具體可參見我的專案,裡面有比較詳細的用法。
不難發現,Retrofit 把請求封裝進 Observable ,在請求結束後呼叫 onNext()
以及 OnCompleted()
或在請求失敗後呼叫 onError()
。
注:RxJava形式的請求,並不能減少程式碼量,但是邏輯非常清晰。假如請求到資料之後需要對資料進行處理,並且是耗時操作,難道要再開一個執行緒,或者用AsyncTask再做一次非同步?很顯然,RxJava的變換很好的解決了這個問題,依然會使邏輯結構清晰。
這個庫。
, 主要與RxJava結合用於一些View的事件繫結。
做週期性操作的例子,並沒有使之停下來的,沒有去控制訂閱的生命週期,這樣,就有可能引發記憶體洩漏。所以,在Activity#onDestroy()的時候或者不需要繼續執行的時候應該取消訂閱。Subscription subscription = Observable.interval(2, TimeUnit.SECONDS)
.subscribe(new Subscriber() { @Override
public void onCompleted() {
} @Override
public void onError(Throwable e) {
} @Override
public void onNext(Long aLong) {
Log.i(TAG, "Hello World!");
}
});// 呼叫unsubscribe();方法進行取消訂閱subscription.unsubscribe();
做週期性操作的例子,並沒有使之停下來的,沒有去控制訂閱的生命週期,這樣,就有可能引發記憶體洩漏。所以,在Activity#onDestroy()的時候或者不需要繼續執行的時候應該取消訂閱。Subscription subscription = Observable.interval(2, TimeUnit.SECONDS)
.subscribe(new Subscriber() { @Override
public void onCompleted() {
} @Override
public void onError(Throwable e) {
} @Override
public void onNext(Long aLong) {
Log.i(TAG, "Hello World!");
}
});// 呼叫unsubscribe();方法進行取消訂閱subscription.unsubscribe();
但是,如果有很多個資料來源,那豈不是要取消很多次?當然不是的,可以利用 CompositeSubscription
, 相當於一個 Subscription
集合。
CompositeSubscription list = new CompositeSubscription(); list.add(subscription1); list.add(subscription2); list.add(subscription3);// 統一呼叫一次unsubscribe,就可以把所有的訂閱都取消list.unsubscribe();
http://www.apkbus.com/blog-873055-77431.html
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4822/viewspace-2811953/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 專案管理系統全解析:從入門到精通專案管理
- 【從前端到全棧】- koa快速入門指南前端全棧
- RxJava從入門到不離不棄(四)——過濾操作符RxJava
- scala 從入門到入門+
- makefile從入門到入門
- RxJava快速入門RxJava
- kafka從入門到關門Kafka
- MyBatis從入門到精通(一):MyBatis入門MyBatis
- Promise從入門到精通Promise
- LESS從入門到精通
- Git 從入門到精通Git
- babel從入門到跑路Babel
- SAP從入門到精通
- Python從入門到精通Python
- Thymeleaf從入門到精通
- Eclipse從入門到精通Eclipse
- SA:從入門到入土
- Jdbc從入門到入土JDBC
- vim從入門到精通
- Shell從入門到精通
- Service Worker 從入門到出門
- Django 從啟動到請求到響應全過程分析-入門版Django
- Xposed從入門到棄坑:0x03、XposedHelpers類解析
- 0到1,Celery從入門到出家
- Python從入門到轉行Python
- Git 從入門到放棄Git
- AndroidRecyclerView從入門到玩壞AndroidView
- Kaizen如何從入門到精通?AI
- Linux從入門到精通(二)Linux
- Docker 從入門到掉坑Docker
- XXE從入門到放棄
- ElasticSearch 7.8.1 從入門到精通Elasticsearch
- Vue 從入門到放棄Vue
- Nginx從入門到放棄Nginx
- Kotlin從入門到跑路(一)Kotlin
- 幀數,從入門到入土
- Redis從入門到進階Redis
- RabbitMQ 從入門到精通 (一)MQ