是時候學習RxJava了

RxJava

從開始最開始學習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

RxBindingJakeWharton大牛用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方向)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,592評論 25 707
  • 1.RxJava是什么 2.在Android中怎么去使用RxJava 3.RxJava操作符的介紹 4.RxJav...
    我的資訊圈閱讀 534評論 0 0
  • 你自以為離開了追星的生活就無事可做,整日躺在床上發呆,借用零食來消耗漫長的時光,卻不知你可以做很多有意義的事情。 ...
    小凱弟弟閱讀 227評論 1 0
  • 最近看STL學習視頻很是吃力,由于老師對原理沒有介紹到,只是簡單介紹基本使用,在這里以便后期使用是好查看在此記錄一...
    易班熊閱讀 534評論 0 50
  • “你是什么人?”閻母盯著良生的手機問。 “我就是你口中的高枝,鄙人不才,家有房兩套、車子一輛、還有良妻。”說罷良生...
    S菩提只吃半碗飯閱讀 376評論 3 2