Android RxJava系列一: 基礎(chǔ)常用詳解

前言

本篇主要介紹Rxjava在 Android 項(xiàng)目中的基礎(chǔ)使用和常用方法,旨在給對 RxJava 感興趣的人一些入門的指引.對Rxjava不熟悉的朋友可以去看我之前寫的一篇簡單介紹 Android RxJava:基礎(chǔ)介紹與使用,下面就來我們一起來看看在項(xiàng)目中如何使用 Rxjava 吧!

Rxjava是什么?為什么要用Rxjava?

首先我們要知道Rxjava到底是什么東西?為什么這么多人用它以及它在Android項(xiàng)目中所占的比重.

  • Rxjava到底是什么呢?
    RxJava 在 GitHub 主頁上的自我介紹是 : a library for composing asynchronous and event-based programs using observable sequences for the Java VM(一個(gè)在 Java VM 上使用可觀測的序列來組成異步的熊尉、基于事件的程序的庫)。這就是 RxJava ,概括得非常精準(zhǔn)。然而對于初學(xué)者來說,兩個(gè)字,不懂.其實(shí)說白了,Rxjava就是一個(gè)用來實(shí)現(xiàn)異步操作的第三方庫,而至于其他的拓展功能也只是在實(shí)現(xiàn)異步過程中提供了一些輔助功能罷了.所以總結(jié)一下就一句話:

Rxjava是一個(gè)用來實(shí)現(xiàn)異步的、基于事件的第三方庫(就把它理解成Android Handler 的升級版就行了)

  • 為什么要用Rxjava?
    這就到我們今天的重頭戲了.相信很多初學(xué)者都是在以下場景初識Rxjava的

1.201x年你必須知道的幾個(gè)Android開源庫: .......瞻坝、Rxjava
這是一個(gè)基于 Rxjava+Retrofit+mvp+........的demo
Android 工作必回 Rxjava+Retrofit+.......幾件套

起初本人也是因?yàn)榭吹竭@些字眼才接觸的Rxjava的,也是因?yàn)檫@些原因我才使用的Rxjava. But,當(dāng)你真正的去了解了Rxjava,真正的把Rxjava用到了你的項(xiàng)目中,你才真正知道,為啥這么多人說要用Rxjava,以及你為什么要用Rxjava ,因?yàn)镽xjava真的太好用太便捷了,用了一次你就離不開它了.

舉個(gè)例子你就能明白了:
假如現(xiàn)在有這么一個(gè)需求:你需要從數(shù)據(jù)庫中取出一組圖片資源id,然后通過遍歷將它們顯示在imageView上面,實(shí)現(xiàn)方式有很多種

  • 使用傳統(tǒng)方式實(shí)現(xiàn)
 //操作數(shù)據(jù)庫屬于耗時(shí)操作,需要開辟一個(gè)新線程放在后臺(tái)操作
        new Thread() {
            @Override
            public void run() {
                super.run();
                final int[] drawableRes = {}; //.......從數(shù)據(jù)庫中取出id資源數(shù)組操作
                //將id對應(yīng) drawable顯示在界面上,需要在UI線程操作
                imageView.post(new Runnable() {
                    @Override
                    public void run() {
                        imageView.setImageResource(drawableRes[0]);
                    }
                });
            }
        }.start();

Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //.....操作UI
            imageView.setImageResource(drawableRes[0]);
        }
    };

 new Thread(){
            @Override
            public void run() {
                super.run();
                final int[] drawableRes = {}; //.......從數(shù)據(jù)庫中取出id資源數(shù)組操作
                //將id對應(yīng) drawable顯示在界面上,需要在UI線程操作
                handler.sendEmptyMessage(0);//發(fā)送消息,通知主線程刷新UI

            }
        }.start();
  • 使用Rxjava方式實(shí)現(xiàn)
 Observable.create(new ObservableOnSubscribe<List>() {
            @Override
            public void subscribe(ObservableEmitter<List> emitter) throws Exception {
               drawableRes = new ArrayList<>(); //.......從數(shù)據(jù)庫中取出id資源數(shù)組操作
               emitter.onNext(drawableRes);
               emitter.onComplete();
            }
        }).flatMap(new Function<List, ObservableSource<Integer>>() {
            @Override
            public ObservableSource<Integer> apply(List list) throws Exception {
                return Observable.fromIterable(list);
            }
        }).subscribeOn(Schedulers.io())//在IO線程執(zhí)行數(shù)據(jù)庫處理操作
          .observeOn(AndroidSchedulers.mainThread())//在UI線程顯示圖片
          .subscribe(new Observer<Integer>() {
              @Override
              public void onSubscribe(Disposable d) {
                  Log.d("----","onSubscribe");
              }

              @Override
              public void onNext(Integer integer) {
                  imageView.setImageResource(integer);//拿到id,加載圖片
                  Log.d("----",integer+"");
              }

              @Override
              public void onError(Throwable e) {
                  Log.d("----",e.toString());
              }

              @Override
              public void onComplete() {
                  Log.d("----","onComplete");
              }
          });      

誒,等一下,你不是說使用Rxjava實(shí)現(xiàn)代碼會(huì)更簡潔快捷嘛,我怎么看著實(shí)現(xiàn)還變復(fù)雜了,明明只要切換一下線程,你這咋寫了這么多,看不懂?????

咳咳,看不懂了吧,看不懂就對了,看著的確是變復(fù)雜了,But 代碼這一連串鏈?zhǔn)秸{(diào)用下來不是顯得代碼邏輯很清晰嗎.而且隨著業(yè)務(wù)需求的增多,你可能需要拿到圖片id時(shí)還要加一層過濾呢,只需要在加一個(gè).xxx()方法即可,而且還可以隨意切換操作線程,代碼依然還是這么清晰簡潔.而且使用Android studio 打開時(shí)還會(huì)自動(dòng)縮進(jìn)和顯示提示信息:

Rxjava.jpg

上面的例子省去了部分代碼,也是我隨手寫的,只要是讓你體會(huì)一下Rxjava的書寫方式和對比一下傳統(tǒng)方式的實(shí)現(xiàn)有什么不同,看不懂沒關(guān)系,下面我會(huì)一一解釋,搬好小板凳
總結(jié)一下:

為什么要用Rxjava: 因?yàn)殡S著程序邏輯變得越來越復(fù)雜,它依然能夠保持代碼的簡潔和閱讀性.

怎么用Rxjava?

關(guān)于Rxjava的簡單集成和基礎(chǔ)使用請查看我之前的介紹Android RxJava:基礎(chǔ)介紹與使用

首先大概說一下Rxjava的原理:

1.概念: 觀察者模式
RxJava 的異步實(shí)現(xiàn),是通過一種擴(kuò)展的觀察者模式來實(shí)現(xiàn)的慧邮。至于觀察者模式的原理實(shí)現(xiàn)大家肯定都已經(jīng)很熟悉了,我就不再闡述了,不熟悉的可以自行搜索.

RxJava 有四個(gè)基本概念:Observable (可觀察者,即被觀察者)舟陆、 Observer (觀察者)误澳、 subscribe (訂閱)、事件吨娜。Observable 和 Observer 通過 subscribe() 方法實(shí)現(xiàn)訂閱關(guān)系脓匿,從而 Observable 可以在需要的時(shí)候發(fā)出事件來通知 Observer。

RxJava 的事件回調(diào)方法除了普通事件 onNext() (相當(dāng)于 onClick() / onEvent())之外宦赠,還定義了兩個(gè)特殊的事件:onCompleted() 和 onError()陪毡。

  • onCompleted(): 事件隊(duì)列完結(jié)米母。RxJava 不僅把每個(gè)事件單獨(dú)處理,還會(huì)把它們看做一個(gè)隊(duì)列毡琉。RxJava 規(guī)定铁瞒,當(dāng)不會(huì)再有新的 onNext() 發(fā)出時(shí),需要觸發(fā)
  • onCompleted() 方法作為標(biāo)志桅滋。
  • onError(): 事件隊(duì)列異常慧耍。在事件處理過程中出異常時(shí),onError() 會(huì)被觸發(fā)丐谋,同時(shí)隊(duì)列自動(dòng)終止芍碧,不允許再有事件發(fā)出。
  • 在一個(gè)正確運(yùn)行的事件序列中, onCompleted() 和 onError() 有且只有一個(gè)号俐,并且是事件序列中的最后一個(gè)泌豆。需要注意的是,onCompleted() 和 onError() 二者也是互斥的吏饿,即在隊(duì)列中調(diào)用了其中一個(gè)踪危,就不應(yīng)該再調(diào)用另一個(gè)。
Rxjava的觀察者模式.jpg
  1. 基本實(shí)現(xiàn)
  • 創(chuàng)建 Observer 觀察者
  Observer observer = new Observer<String>(){

            @Override
            public void onSubscribe(Disposable d) {
                Log.d("----","onSubscribe" );
            }

            @Override
            public void onNext(String s) {
                Log.d("----", s);
            }

            @Override
            public void onError(Throwable e) {
                Log.d("----", "onError");
            }

            @Override
            public void onComplete() {
                Log.d("----", "onComplete");
            }
        };
  • 創(chuàng)建 Observable 被觀察者
 Observable observable =Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                    emitter.onNext("rxjava");
                    emitter.onComplete();
            }
        });

  • Subscribe (訂閱)
observable.subscribe(observer);

以上就是傳統(tǒng)的使用方式,你也可以采用鏈?zhǔn)秸{(diào)用:

  Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                emitter.onNext("rxjava");
                emitter.onComplete();
            }
        }).subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.d("----", "onSubscribe");
            }

            @Override
            public void onNext(String s) {
                Log.d("----", s);
            }

            @Override
            public void onError(Throwable e) {
                Log.d("----", "onError");
            }

            @Override
            public void onComplete() {
                Log.d("----", "onComplete");
            }
        });

在 RxJava 的默認(rèn)規(guī)則中猪落,事件的發(fā)出和消費(fèi)都是在同一個(gè)線程的贞远。也就是說,如果只用上面的方法笨忌,實(shí)現(xiàn)出來的只是一個(gè)同步的觀察者模式蓝仲。觀察者模式本身的目的就是『后臺(tái)處理,前臺(tái)回調(diào)』的異步機(jī)制蜜唾,因此異步對于 RxJava 是至關(guān)重要的杂曲。而要實(shí)現(xiàn)異步,則需要用到 RxJava 的另一個(gè)概念: Scheduler 袁余。

先貼代碼,以下便可實(shí)現(xiàn)異步操作:

 Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                emitter.onNext("rxjava");
                emitter.onComplete();
            }
        }).subscribeOn(Schedulers.newThread())
          .observeOn(AndroidSchedulers.mainThread())      
          .subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.d("----", "onSubscribe");
            }

            @Override
            public void onNext(String s) {
                Log.d("----", s);
            }

            @Override
            public void onError(Throwable e) {
                Log.d("----", "onError");
            }

            @Override
            public void onComplete() {
                Log.d("----", "onComplete");
            }
        });

在這里我們使用了Scheduler 進(jìn)行了線程的切換,接下來介紹一下Scheduler :

  1. Scheduler (線程切換)

在不指定線程的情況下擎勘, RxJava 遵循的是線程不變的原則,即:在哪個(gè)線程調(diào)用 subscribe()颖榜,就在哪個(gè)線程生產(chǎn)事件棚饵;在哪個(gè)線程生產(chǎn)事件,就在哪個(gè)線程消費(fèi)事件掩完。如果需要切換線程噪漾,就需要用到 Scheduler (調(diào)度器)。

RxJava 已經(jīng)內(nèi)置了幾個(gè) Scheduler :

  • Schedulers.newThread(): 總是啟用新線程且蓬,并在新線程執(zhí)行操作欣硼。

  • Schedulers.io(): I/O 操作(讀寫文件、讀寫數(shù)據(jù)庫恶阴、網(wǎng)絡(luò)信息交互等)所使用的 Scheduler诈胜。行為模式和 newThread() 差不多豹障,區(qū)別在于 io() 的內(nèi)部實(shí)現(xiàn)是是用一個(gè)無數(shù)量上限的線程池,可以重用空閑的線程焦匈,因此多數(shù)情況下 io() 比 newThread() 更有效率血公。不要把計(jì)算工作放在 io() 中,可以避免創(chuàng)建不必要的線程缓熟。

  • Schedulers.computation(): 計(jì)算所使用的 Scheduler累魔。這個(gè)計(jì)算指的是 CPU 密集型計(jì)算,即不會(huì)被 I/O 等操作限制性能的操作够滑,例如圖形的計(jì)算垦写。這個(gè) Scheduler 使用的固定的線程池,大小為 CPU 核數(shù)彰触。不要把 I/O 操作放在 computation() 中梯澜,否則 I/O 操作的等待時(shí)間會(huì)浪費(fèi) CPU。

  • AndroidSchedulers.mainThread()渴析,它指定的操作將在 Android 主線程運(yùn)行

有了這幾個(gè) Scheduler ,就可以使用 subscribeOn() 和 observeOn() 兩個(gè)方法來對線程進(jìn)行控制了吮龄。

  • subscribeOn(): 指定 subscribe() 所發(fā)生的線程俭茧,即 Observable.OnSubscribe 被激活時(shí)所處的線程±熘悖或者叫做事件產(chǎn)生的線程母债。

  • observeOn(): 指定 Subscriber 所運(yùn)行在的線程〕⒍叮或者叫做事件消費(fèi)的線程毡们。

簡單使用如下:

 Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                emitter.onNext("1");
                emitter.onNext("2");
                emitter.onNext("3");
                emitter.onComplete();
            }
        }).subscribeOn(Schedulers.io()) //在io執(zhí)行上述操作
          .observeOn(AndroidSchedulers.mainThread())//在UI線程執(zhí)行下面操作
          .subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.d("----","開始了");
            }

            @Override
            public void onNext(String s) {
                Log.d("----", s);
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {
                Log.d("----", "complete");
            }
        });

以上就是Rxjava的基本常用方法了,看到這里你就已經(jīng)可以愉快的使用Rxjava代替AsyncTask / Handler了,趕緊去試試吧!


  • 參考文章: 給 Android 開發(fā)者的 RxJava 詳解,這是扔物線很早之前寫的一篇關(guān)于Rxjava 1.x的詳解,雖然現(xiàn)在已經(jīng)Rxjava已經(jīng)發(fā)展到2.x版本了.但其中一些基本實(shí)現(xiàn)原理的講解確實(shí)非常到位,通俗易懂,很適合初學(xué)者學(xué)習(xí),強(qiáng)烈建議去看下

  • RxJava2 只看這一篇文章就夠了這是玉剛說的一篇關(guān)于Rxjava常用API的介紹,基本囊括了Rxjava所用到的所有API,還有代碼舉例,也是強(qiáng)烈建議觀看收藏

關(guān)于Rxjava系列一就到此結(jié)束啦,后面有時(shí)間我還會(huì)寫寫一些其他的常用拓展操作符和與retrofit2的結(jié)合使用,歡迎關(guān)注訂閱!

歡迎關(guān)注作者darryrzhong,更多干貨等你來拿喲.

請賞個(gè)小紅心!因?yàn)槟愕墓膭?lì)是我寫作的最大動(dòng)力昧辽!

更多精彩文章請關(guān)注

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末衙熔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子搅荞,更是在濱河造成了極大的恐慌红氯,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咕痛,死亡現(xiàn)場離奇詭異痢甘,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)茉贡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門塞栅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人腔丧,你說我怎么就攤上這事放椰∽餮蹋” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵庄敛,是天一觀的道長俗壹。 經(jīng)常有香客問我,道長藻烤,這世上最難降的妖魔是什么绷雏? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任怖亭,我火速辦了婚禮涎显,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兴猩。我一直安慰自己期吓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布倾芝。 她就那樣靜靜地躺著讨勤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晨另。 梳的紋絲不亂的頭發(fā)上潭千,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音借尿,去河邊找鬼刨晴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛路翻,可吹牛的內(nèi)容都是我干的狈癞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼茂契,長吁一口氣:“原來是場噩夢啊……” “哼蝶桶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掉冶,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤莫瞬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后郭蕉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疼邀,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年召锈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旁振。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拐袜,靈堂內(nèi)的尸體忽然破棺而出吉嚣,到底是詐尸還是另有隱情,我是刑警寧澤蹬铺,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布尝哆,位于F島的核電站,受9級特大地震影響甜攀,放射性物質(zhì)發(fā)生泄漏秋泄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一规阀、第九天 我趴在偏房一處隱蔽的房頂上張望恒序。 院中可真熱鬧,春花似錦谁撼、人聲如沸歧胁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喊巍。三九已至,卻和暖如春箍鼓,著一層夾襖步出監(jiān)牢的瞬間玄糟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工袄秩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逢并。 一個(gè)月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓之剧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親砍聊。 傳聞我的和親對象是個(gè)殘疾皇子背稼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容