RxJava 介紹
寫在前面
rxjava 也用了好多次了。時隔多年,才終於有勇氣挖一下它的源碼。
這次主要研究了發佈訂閱流程 subscribe() 以及核心變換 lift(),至於其他的像線程切換 Schedulers、豐富的操作符啊,都有待研究。
之後可能會再更新幾篇,也可能就太監了。。。
1.RxJava 源碼分析之 —— lift 變換
What is RxJava?
RxJava 的思想是響應式編程,這是一種事件驅動的編程模式。事件驅動很熟悉吧,經典如通過 Listener 來監聽點擊事件再回調 OnClick(),這種方式早就爛大街了吧。RxJava 本質上與這種監聽器沒有區別,那麼它又有什麼獨到之處呢?
Why RxJava?
它的獨到之處在與它響應式的編程思想——一切皆抽象爲事件。如果說對於點擊事件,Android 本身已經提供了一套監聽方案,那麼網絡請求呢,文件讀寫呢,還有數不清的事件呢。。此前,我們都是自己編寫網絡請求框架,然後對外提供回調接口 OnSuccess() 、OnFail()。換成文件讀寫,又得重新搞一套框架,我們累了,需要一個通用的解決方案,它就是 RxJava 。
By the way,解決了”回調地獄”只是順帶的好處罷了。
Where RxJava?
那麼什麼場景下使用它呢?
它的應用場合很多,本文將以網絡請求爲例做一個 Demo。
更多場景請參考這篇文章:
可能是東半球最全的RxJava使用場景小結
How RxJava?
要使用 RxJava 還得深入到代碼層面。
它是基於觀察者模式實現的,那麼自然會有一個被觀察者(可觀察的對象) Observable 和 一個觀察者(訂閱者)Subscriber。
需要的第三方包
// app/build.gradle
dependencies {
// 。。。
// rxjava
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.1.0'
// rxlifecycle
compile 'com.trello:rxlifecycle:0.4.0'
compile 'com.trello:rxlifecycle-components:0.4.0'
// 上面4個包是必要的
// okhttp
compile 'com.squareup.okhttp:okhttp:2.7.2'
// butterknife
compile 'com.jakewharton:butterknife:7.0.1'
}
最好配合上 retrolambda 這個插件,用 lambda 語法寫起來才爽。
概念介紹
先來看一下 Subscriber 包含了哪些東西
// 打開源碼我們看到 Subsctiber 實現了 Observer
public abstract class Subscriber<T> implements Observer<T>, Subscription { ... }
// 而 Observer 包含有一些回調函數
// {OnNext(), OnError(), OnComplete()}
public interface Observer<T> {
void onNext(T t);
void onError(Throwable e);
void onCompleted();
}
然後看看它怎麼訂閱事件
// Subscriber 作爲監聽器,需要註冊到 Observable 中:
// (我們才能在 Observable 中回調它的一些函數)
mObservable.subscribe(mSubscriber);
// 這類似於我們註冊點擊事件的監聽:
mView.setOnClickListener(mOnClickListener);
// 可以對應起來理解,View 即是被觀察者,
// 而 OnClickListener 則是訂閱者。
其中,mSubscriber 是這樣的
// 當然,正如 mOnClickListener 實現了 OnClick(),
// mSubscriber 作爲匿名類實現了三個接口函數,像這樣:
Subscriber<String> mSubscriber = new Subscriber<String>() {
@Override
public void onNext(String s) { }
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) { }
};
一些簡化寫法:
// 考慮到這樣的匿名類寫法比較冗長,而且不能用 lambda 簡化,
// 作者還提供了 subscribe(mSubscriber) 的一些重載版本:
mObservable.subscribe(onNext);
mObservable.subscribe(onNext, onError);
mObservable.subscribe(onNext, onError, onComplete);
// 太棒了,它們都是函數式接口,可以用 lambda 簡化代碼。
再來看一下 Observable , 之前說到它可以被 Subcriber 訂閱。但是在此之前,我們需要先創建它。
// 創建 Observable 實例
mObservable.create(mOnSubscribe);
// 其中 mOnSubscribe 接口,用來制定事件分發的流程。
// 即按照一定順序,回調之前註冊好的三個函數:
// {OnNext(), OnError(), OnComplete()}
Observable.OnSubscribe<String> mOnSubscribe = new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
String data = getData(); // 獲取數據: 網絡任務或是別的...
if(data == null){ // 獲取失敗
subscriber.onError(new Throwable("data 爲空"));
subscriber.onCompleted();
}else{ // 獲取成功
subscriber.onNext(data);
subscriber.onCompleted();
}
}
};
What’s more,Observable 還可以分別指定 Observable 和 Subcriber 的線程:
Observable.create(subscriber -> {})
.subscribeOn(Schedulers.io()) // 指定 Observable 的線程 ———— 主線程。
.observeOn(AndroidSchedulers.mainThread()); // 指定 Subscriber 的線程 ———— io 線程。
框架內部估計封裝了線程切換的功能,這在 Android 的多線程環境中,極大地簡化了線程操作。
實踐
我們來試一下網絡請求吧,從網上拉取一段字符串(http://publicobject.com/helloworld.txt)。首先,我們把一個請求看作一個事件,先創建一個事件對應的被觀察者 NetworkObserable.getInstance() : Observable< String >。
public class NetworkObserable{
...
// 獲取一個 Observable 實例
public static Observable<String> getInstance() {
return Observable.create((Observable.OnSubscribe<String>)
subscriber -> subscribeData(subscriber, NetworkHelper.getData(NetworkHelper.GET))
// getData()發起一個GET請求,返回 String 數據。
).subscribeOn(Schedulers.io());
// 被觀察的對象在 io 線程進行網絡請求,也可以自己新開一個線程
}
// 取得網絡數據後,回調 subscriber 的一些函數,在界面上顯示這些數據
private static void subscribeData(Subscriber<? super String> subscriber, String data){
if(data == null){
subscriber.onError(new Throwable("data 爲空"));
subscriber.onCompleted();
}else{
subscriber.onNext(data);
subscriber.onCompleted();
}
}
}
在點擊按鈕的時候訂閱這個事件。
@OnClick(R.id.btn_GET) public void onClick() {
NetworkObservable.getInstance() // 獲取 Observable 實例
.compose(this.<String>bindToLifecycle()) // Rxlifecycle, 綁定到 Activity 的生命週期。
.observeOn(AndroidSchedulers.mainThread()) // 指定觀察者的線程————主線程。
.subscribe( // 訂閱事件
// onNext, 請求成功
data -> mTvResult.setText(data),
// onError, 請求失敗
throwable -> Toast.makeText(this, throwable.getMessage(), Toast.LENGTH_SHORT).show()
);
}
然後,沒有然後了。。。
附錄
按照慣例,放上 Demo 地址,有興趣可以下載一下:
https://github.com/fashare2015/my_rxjava_demo