從開始最開始學習RxJava到現在也有一段時間了,還記得去年第一次看RxJava的文章就是扔物線的的這篇文章給 Android 開發者的 RxJava 詳解,那一次我看了整整一個下午,由于在那之前我完全沒接觸過RxJava,也不知道那是個什么,看完很多地方都還不是很理解,整個人都是暈暈的,當然收獲也還是有的,至少對RxJava有了一個初步的概念。那次之后我就沒再去碰過RxJava了,當時心里想的是,如果后面需要這方面的東西我再來學習也不遲。
時間差不多過了半年,RxJava也越來越火了,使用RxJava的開發者也是越來越多,github上關于的開源庫中使用RxJava的也越來越多。當我再去看一些開源庫的時候,由于很多地方都用到了RxJava,就發現很多代碼都看不懂了,這也就激起了再次去學習RxJava的動力,隨后就在網上各種的查找RxJava相關的學習資料,從頭學習,我又去看了一遍給 Android 開發者的 RxJava 詳解,收獲還是很多的,本文也是對這段時間我學習RxJava的一個小結,以下知識點主要針對于Android開發者。
本文的學習目錄
1.RxJava是什么
2.在Android中怎么去使用RxJava
3.RxJava操作符的介紹
4.RxJava在生產環境中的使用
5.RxJava學習的參考資料
1.RxJava是什么
要知道RxJava是什么,那么你應該先去了解一下Rx。Rx的全稱是Reactive Extensions,直譯過來就是響應式擴展。Rx基于觀察者模式,他是一種編程模型,目標是提供一致的編程接口,幫助開發者更方便的處理異步數據流。ReactiveX.io給的定義是,Rx是一個使用可觀察數據流進行異步編程的編程接口,ReactiveX結合了觀察者模式、迭代器模式和函數式編程的精華。Rx已經滲透到了各個語言中,有了Rx所以才有了 RxJava,Rx.NET、RxJS、RxSwift、Rx.rb、RxPHP等等,更詳細的可以去這里看看languages
那么RxJava到底是什么,我對于他的理解就針對于Java語言的一個異步的響應式編程庫。
2.怎么去使用RxJava
在gradle文件的dependencies中加入以下代碼即可(以下版本可能不是最新的,需要最新的可到RxAndroid查看)
compile 'io.reactivex:rxandroid:1.2.0'
compile 'io.reactivex:rxjava:1.1.5'
3.RxJava操作符的介紹
有了以上的配置,我們就可以在Android中使用RxJava了。對于RxJava的使用,最重要的還是對于操作符的學習,熟悉了操作符才能更好的使用RxJava。RxJava中的操作符是非常豐富的,關于RxJava操作符介紹的文章已經是屬于一搜就是一大堆的那種了,所以本文就不多做介紹了,在這里給大家推薦一個學習操作符比較好的地方Operaters
4.RxJava在生產環境中的使用
想必學習RxJava的同學,在學習完操作符之后,最想知道的是怎么將其用在我們平時的開發當中去,本節就帶大家去了解一下怎么去應用RxJava。
RxBinding
節流(防止按鈕的重復點擊)
輪詢,定時操作
RxPermissions
RxBus
RxJava與Retrofit
等待你們的補充~~~
(1) RxBinding
RxBinding是JakeWharton大牛用RxJava為Android控件編寫的一個控件綁定庫,并且為各個包下的控件都編寫相應的庫,如下所示
Platform bindings:
compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
'support-v4' library bindings:
compile 'com.jakewharton.rxbinding:rxbinding-support-v4:0.4.0'
'appcompat-v7' library bindings:
compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.4.0'
'design' library bindings:
compile 'com.jakewharton.rxbinding:rxbinding-design:0.4.0'
'recyclerview-v7' library bindings:
compile 'com.jakewharton.rxbinding:rxbinding-recyclerview-v7:0.4.0'
'leanback-v17' library bindings:
compile 'com.jakewharton.rxbinding:rxbinding-leanback-v17:0.4.0'
我們只需引入對應地庫就可以使用了。
比如我們對Button添加一個單擊事件就可以這樣做了
Button button = (Button) findViewById(R.id.button);
RxView.clicks(button).subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
Log.i("test", "clicked");
}
});
到這里,你肯定會說,這并沒有沒什么卵用,還不如直接設置一個setOnClickListener來的方便,直接。繼續往下看
通常情況下,如果我們要防止一個按鈕重復點擊會怎么做?設置一個第一次按下的時間,然后在第二次點擊的時候去判斷?NO NO NO,這樣做都太low了,我們來看看用RxBing怎樣去實現
RxView.clicks(button).debounce(300, TimeUnit.MILLISECONDS).subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
Log.i("test", "clicked");
}
});
很爽吧,這里過濾掉了在300ms內的重復點擊,只需加一個操作符就可以了,而不用我們去編寫一大堆并且還容易出錯的邏輯代碼了。
這里使用最多的一個地方就是在我們做搜索的時候,再結合filter操作,去過濾掉那些沒必要的查詢操作,來減小服務器的壓力和客戶端的流量輸出。
(2) 輪詢,定時操作
在做App的時候,有些地方我們可能會時不時的去請求服務器,以至于客戶端的數據是最新的,在RxJava中可以這樣做
//每隔兩秒執行一次
Observable.interval(2, 2, TimeUnit.SECONDS).subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
//TODO WHAT YOU WANT
}
});
在兩秒后去執行一些操作(比如啟動頁跳轉到主頁面)
Observable.timer(2, TimeUnit.SECONDS).subscribe(new Action1<Long>() {
@Override
public void call(Long aLong) {
//TODO WHAT YOU WANT
}
});
(3) RxPermissions
RxPermissions也是國外的大牛開發的基于RxJava的Android權限管理庫,他讓6.0以上的權限管理更加的簡單,如果有適配6.0以上的手機的需求,這個庫是個不錯的選擇。下面我們來看看基本的用法。
// 請求相機權限
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA)
.subscribe(granted -> {
if (granted) { // 用戶同意了(在6.0之前的手機始終都為true)
//可以拍照了
} else {
//可以在這里提示用戶,或者再次請求
}
});
當然,如果我想一次請求多個權限呢,每次都去寫上面的代碼肯定是個不好的做法,RxPermissions的作者也考慮到了這一點,在Api里提供了一個多參數的重載
//取得相機權限和讀取手機狀態
RxPermissions.getInstance(this)
.request(Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE)
.subscribe(granted -> {
if (granted) {
} else {
}
});
更多的資料還請去github上去查看。
(4) RxBus
有了RxJava,EventBus、Otto什么的都可以靠邊了,因為RxJava本身就自帶了這個功能,我們只需做一下簡單的封裝就可以使用了,也順便減少了我們項目的體積。
public class RxBus {
private final Subject<Object, Object> _bus;
private static class RxBusHolder {
private static final RxBus instance = new RxBus();
}
private RxBus() {
_bus = new SerializedSubject<>(PublishSubject.create());
}
public static synchronized RxBus getInstance() {
return RxBusHolder.instance;
}
public void post(Object o) {
_bus.onNext(o);
}
public <T> Observable<T> toObserverable(Class<T> eventType) {
return _bus.ofType(eventType);
}
怎么去使用?
在需要發送消息的地方
RxBus.getInstance().post("SomeChange");
在需要接收消息的地方
Subscription mSubscription = RxBus.getInstance().toObserverable(String.class).subscribe(new Action1<String>() {
@Override
public void call(String s) {
handleRxMsg(s);
}
});
不要忘了在適當的地方去取消這個訂閱(以免發生內存泄漏)
mSubscription.unsubscribe();
到這里可能你有個疑問,Subject是個什么鬼!
其實Subject同時充當了Observer和Observable的角色,他可以發射數據也可以接收數據,有AsyncSubject、BehaviorSubject、PublishSubject、ReplaySubject四種,詳細的介紹請看Subject
(5) RxJava與Retrofit
Retrofit可能大家都不太陌生了,如果你還不知道的話,那么趕緊去學習吧,這么強大的框架怎么能不知道呢!
后面的講解是基于了解過Retrofit的同學
關于Retrofit的基本使用可能我們都不是太陌生,對于請求后的結果都是在一個回調接口里接收,對于結果的處理并不是太靈活,一大堆的回調會讓你以后回過來看代碼的時候看的醉生夢死。
RxJava很好的解決了這個問題,我們來看看Retrofit的怎么去適配RxJava吧。
gradle文件的引用
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'//RxJava與Retrofit的適配器
這里我以請求 http://gank.io/api/data/Android/10/1 為例。
返回的結果大致是這樣的
于是我定義了一個GankResultBean去接收這個結果。
其中ResultsBean就是results中的每一個條目。
好了,下面我們來看看適配了RxJava的Retrofit怎么去使用吧
首先我們定義一個接口
public interface RxGankService {
@GET("all/20/{page}")
Observable<GankResultBean> getAndroidData(@Path("page") int page);
}
值得注意的是這里返回的是<code>Observable<GankResultBean></code>而不是常規的<code> Call<GankResultBean></code>
接著就可以做請求了
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://gank.io/api/data/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//這個就是用來適配RxJava的
.build();
RxGankService rxGankService = retrofit.create(RxGankService.class);
final Observable<GankResultBean> observable = rxGankService.getAndroidData(1);
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<GankResultBean, List<GankResultBean.ResultsBean>>() {
@Override
public List<GankResultBean.ResultsBean> call(GankResultBean gankResultBean) {
return gankResultBean.getResults();
}
})
.flatMap(new Func1<List<GankResultBean.ResultsBean>, Observable<GankResultBean.ResultsBean>>() {
@Override
public Observable<GankResultBean.ResultsBean> call(List<GankResultBean.ResultsBean> resultsBeen) {
return Observable.from(resultsBeen);
}
})
.filter(new Func1<GankResultBean.ResultsBean, Boolean>() {
@Override
public Boolean call(GankResultBean.ResultsBean resultsBean) {
return "Android".equals(resultsBean.getType());
}
})
.subscribe(new Subscriber<GankResultBean.ResultsBean>() {
@Override
public void onCompleted() {
Log.i("test", "onCompleted");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onNext(GankResultBean.ResultsBean resultsBean) {
textView.append(resultsBean + "\n");
}
});
這里是為了演示才使用了這么多的操作符,在實際使用的時候根據具體情況而定。
下面簡單解釋下上面這段代碼
observeOn(AndroidSchedulers.mainThread()):訂閱者的回調在主線程
subscribeOn(Schedulers.io()):訂閱發生在io線程
map:一般我們不會關心error字段,我們關心的只是results,所以在這里做了一個映射讓用戶接收的是List<GankResultBean.ResultsBean>而不是包含有error的GankResultBean
flatMap:讓結果一條一條的發射出去,而不是一個集合
filter:只接收Type為Android的數據
以上的例子用流的方式是不是很好的解決了對請求結果的處理,對結果的處理可以做到隨心所欲,并且邏輯還很清晰。
以上幾個點就是我了解的關于RxJava的應用。如果你還有其他方面的應用,還望補充。
5.RxJava學習的參考資料
這里我將我學習RxJava以來收集的關于RxJava分享出來,以方便大家的查閱。
學習RxJava的第一手資料,官方的wiki:wiki
我學習RxJava看的第一篇文章:給 Android 開發者的 RxJava 詳解(這個大家肯定都知道)
大頭鬼總結的RxJava學習資料:Awesome-RxJava
RxJava文檔中文翻譯:Rx和RxJava文檔中文翻譯項目
其他:
RxJava操作符圖解(需科學上網)
RxJava處理網絡連接失敗和timer()、interval()、delay()之間的區別
Architecting Android with RxJava
使用RxJava 提升用戶體驗
用RxJava實現事件總線(Event Bus)
Intro to Rx
Implementing an Event Bus With RxJava - RxBus
可能是東半球最全的RxJava使用場景小結
RxWeekend——RxJava周末狂歡
最后:為了跟上時代的步伐,是時候學習RxJava了
歡迎關注我的專題:RxJava系列專題(Android方向)