徹底了解RxJava(一)基礎(chǔ)知識

原文鏈接:http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/
RxJava是目前在Android開發(fā)者中新興熱門的函數(shù)庫徐裸。唯一的問題是剛開始接觸時會感到較難理解粗合。函數(shù)響應(yīng)式編程對于“外面世界”來的開發(fā)人員而言是很難理解的,但一旦理解了它究飞,你會感覺真是太棒了倘感。
我將介紹RxJava的一些知識放坏,這系列文章(四部分)的目標(biāo)是把你領(lǐng)進(jìn)RxJava的大門咙咽。我不會解釋所有相關(guān)的知識點(diǎn)(我也做不到)老玛,我只想引起你對RxJava的興趣并知道它是如何工作的。

基礎(chǔ)知識

響應(yīng)式代碼的基本組成部分是Observables和Subscribers(事實上Observer才是最小的構(gòu)建塊钧敞,但實踐中使用最多的是Subscriber蜡豹,因為Subscriber才是和Observables的對應(yīng)的。)溉苛。Observable發(fā)送消息镜廉,而Subscriber則用于消費(fèi)消息。
消息的發(fā)送是有固定模式的愚战。Observable可以發(fā)送任意數(shù)量的消息(包括空消息)娇唯,當(dāng)消息被成功處理或者出錯時齐遵,流程結(jié)束。Observable會調(diào)用它的每個Subscriber的Subscriber.onNext()函數(shù)塔插,并最終以Subscriber.onComplete()或者Subscriber.onError()結(jié)束梗摇。
這看起來像標(biāo)準(zhǔn)的觀察者模式, 但不同的一個關(guān)鍵點(diǎn)是:Observables一般只有等到有Subscriber訂閱它想许,才會開始發(fā)送消息(術(shù)語上講就是熱啟動Observable和冷啟動Observable伶授。熱啟動Observable任何時候都會發(fā)送消息,即使沒有任何觀察者監(jiān)聽它流纹。冷啟動Observable只有在至少有一個訂閱者的時候才會發(fā)送消息(我的例子中都是只有一個訂閱者)糜烹。這個區(qū)別對于開始學(xué)習(xí)RxJava來說并不重要。)漱凝。換句話說疮蹦,如果沒有訂閱者觀察它,那么將不會起什么作用茸炒。

Hello, World!

讓我們以一個具體例子來實際看看這個框架挚币。首先,我們創(chuàng)建一個基本的Observable:

Observable<String> myObservable = Observable.create(
    new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> sub) {
            sub.onNext("Hello, world!");
            sub.onCompleted();
        }
    }
);

我們的Observable發(fā)送“Hello,world!”消息然后完成】鄣洌現(xiàn)在讓我們創(chuàng)建Subscriber來消費(fèi)這個數(shù)據(jù):

Subscriber<String> mySubscriber = new Subscriber<String>() {
    @Override
    public void onNext(String s) { System.out.println(s); }

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) { }
};

上面代碼所做的工作就是打印由Observable發(fā)送的字符串∽北希現(xiàn)在我們有了myObservable和mySubscriber,就可以通過subscribe()函數(shù)把兩者關(guān)聯(lián)起來:

myObservable.subscribe(mySubscriber);
// Outputs "Hello, world!"

當(dāng)訂閱完成贮尖,myObservable將調(diào)用subscriber的onNext()和onComplete()函數(shù)笛粘,最終mySubscriber打印“Hello, world!”然后終止。

更簡潔的代碼

上面為了打印“Hello, world!”寫了大量的樣板代碼湿硝,目的是為了讓你詳細(xì)了解到底發(fā)生了什么薪前。RxJava提供了很多快捷方式來使編碼更簡單。
首先讓我們簡化Observable关斜,RxJava為常見任務(wù)提供了很多內(nèi)建的Observable創(chuàng)建函數(shù)示括。在以下這個例子中,Observable.just()發(fā)送一個消息然后完成痢畜,功能類似上面的代碼(嚴(yán)格來說垛膝,Observable.just()函數(shù)跟我們原來的代碼并不完全一致,但在本系列第三部分之前我不會說明原因):

Observable<String> myObservable =
    Observable.just("Hello, world!");

接下來丁稀,讓我們處理Subscriber不必要的樣板代碼吼拥。如果我們不關(guān)心onCompleted()或者onError()的話,那么可以使用一個更簡單的類來定義onNext()期間要完成什么功能:

Action1<String> onNextAction = new Action1<String>() {
    @Override
    public void call(String s) {
        System.out.println(s);
    }
};

Actions可以定義Subscriber的每一個部分线衫,Observable.subscribe()函數(shù)能夠處理一個凿可,兩個或者三個Action參數(shù),分別表示onNext()授账,onError()和onComplete()函數(shù)枯跑。上面的Subscriber現(xiàn)在如下所示:

myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);

然而惨驶,現(xiàn)在我們不需要onError()和onComplete()函數(shù),因此只需要第一個參數(shù):

myObservable.subscribe(onNextAction);
// Outputs "Hello, world!"

現(xiàn)在敛助,讓我們把上面的函數(shù)調(diào)用鏈接起來從而去掉臨時變量:

Observable.just("Hello, world!")
    .subscribe(new Action1<String>() {
        @Override
        public void call(String s) {
              System.out.println(s);
        }
    });

最后敞咧,我們使用Java 8的lambdas表達(dá)式來去掉丑陋的Action1代碼:

Observable.just("Hello, world!")
    .subscribe(s -> System.out.println(s));

如果你在Android平臺上(因此不能使用Java 8),那么我嚴(yán)重推薦使用retrolambda辜腺,它將極大的減少代碼的冗余度休建。

變換

讓我們來點(diǎn)刺激的。假如我想把我的簽名拼接到“Hello, world!的輸出中评疗。一個可行的辦法是改變Observable:

Observable.just("Hello, world! -Dan")
    .subscribe(s -> System.out.println(s));

當(dāng)你有權(quán)限控制Observable時這么做是可行的测砂,但不能保證每次都可以這樣,如果你使用的是別人的函數(shù)庫呢百匆?另外一個可能的問題是:如果項目中在多個地方使用Observable砌些,但只在某個地方需要增加簽名,這時怎么辦加匈?
我們嘗試修改Subscriber如何呢存璃?

Observable.just("Hello, world!")
    .subscribe(s -> System.out.println(s + " -Dan"));

這個解決方案也不盡如人意,不過原因不同于上面:我想Subscribers盡可能的輕量級雕拼,因為我可能要在主線程中運(yùn)行它纵东。從更概念化的層面上講,Subscribers是用于被動響應(yīng)的啥寇,而不是主動發(fā)送消息使其他對象發(fā)生變化偎球。 如果可以通過某些中間步驟來對上面的“Hello, world!”進(jìn)行轉(zhuǎn)換,那豈不是很酷辑甜?

Operators簡介

接下來我們將介紹如何解決消息轉(zhuǎn)換的難題:使用Operators衰絮。Operators在消息發(fā)送者Observable和消息消費(fèi)者Subscriber之間起到操縱消息的作用。RxJava擁有大量的opetators磷醋,但剛開始最好還是從一小部分開始熟悉猫牡。 這種情況下,map() operator可以被用于將已被發(fā)送的消息轉(zhuǎn)換成另外一種形式:

Observable.just("Hello, world!")
    .map(new Func1<String, String>() {
        @Override
        public String call(String s) {
            return s + " -Dan";
        }
    })
    .subscribe(s -> System.out.println(s));

同樣的邓线,我們可以使用Java 8的lambdas表達(dá)式來簡化代碼:

Observable.just("Hello, world!")
    .map(s -> s + " -Dan")
    .subscribe(s -> System.out.println(s));

很酷吧淌友?我們的map() operator本質(zhì)上是一個用于轉(zhuǎn)換消息對象的Observable。我們可以級聯(lián)調(diào)用任意多個的map()函數(shù)褂痰,一層一層地將初始消息轉(zhuǎn)換成Subscriber需要的數(shù)據(jù)形式亩进。

map()更多的解釋

map()函數(shù)有趣的一點(diǎn)是:它不需要發(fā)送和原始的Observable一樣的數(shù)據(jù)類型。假如我的Subscriber不想直接輸出原始的字符串缩歪,而是想輸出原始字符串的hash值:

Observable.just("Hello, world!")
    .map(new Func1<String, Integer>() {
        @Override
        public Integer call(String s) {
            return s.hashCode();
        }
    })
    .subscribe(i -> System.out.println(Integer.toString(i)));

有趣的是,我們的原始輸入是字符串谍憔,但Subscriber最終收到的是Integer類型匪蝙。同樣的主籍,我們可以使用lambdas簡化代碼如下:

Observable.just("Hello, world!")
    .map(s -> s.hashCode())
    .subscribe(i -> System.out.println(Integer.toString(i)));

正如我前面說過的,我希望Subscriber做盡量少的工作逛球,我們可以把hash值轉(zhuǎn)換成字符串的操作移動到一個新的map()函數(shù)中:

Observable.just("Hello, world!")
    .map(s -> s.hashCode())
    .map(i -> Integer.toString(i))
    .subscribe(s -> System.out.println(s));

你不想瞧瞧這個嗎千元?我們的Observable和Subscriber變回了原來的代碼。我們只是在中間添加了一些變換的步驟颤绕,我甚至可以把我的簽名轉(zhuǎn)換添加回去:

Observable.just("Hello, world!")
    .map(s -> s + " -Dan")
    .map(s -> s.hashCode())
    .map(i -> Integer.toString(i))
    .subscribe(s -> System.out.println(s));

那又怎樣幸海?

到這里你可能會想“為了得到簡單的代碼有很多聰明的花招”。確實奥务,這是一個簡單的例子物独,但有兩個概念你需要理解:
關(guān)鍵概念#1:Observable和Subscriber能完成任何事情。
放飛你的想象氯葬,任何事情都是可能的挡篓。 你的Observable可以是一個數(shù)據(jù)庫查詢,Subscriber獲得查詢結(jié)果然后將其顯示在屏幕上帚称。你的Observable可以是屏幕上的一個點(diǎn)擊官研,Subscriber響應(yīng)該事件。你的Observable可以從網(wǎng)絡(luò)上讀取一個字節(jié)流闯睹,Subscriber將其寫入本地磁盤中戏羽。 這是一個可以處理任何事情的通用框架。
關(guān)鍵概念#2:Observable和Subscriber與它們之間的一系列轉(zhuǎn)換步驟是相互獨(dú)立的楼吃。
我們可以在消息發(fā)送者Observable和消息消費(fèi)者Subscriber之間加入任意多個想要的map()函數(shù)蛛壳。這個系統(tǒng)是高度可組合的:它很容易對數(shù)據(jù)進(jìn)行操縱。只要operators符合輸入輸出的數(shù)據(jù)類型所刀,那么我可以得到一個無窮盡的調(diào)用鏈(好吧衙荐,并不是無窮盡的,因為總會到達(dá)物理機(jī)器的極限的浮创,但你知道我想表達(dá)的意思)忧吟。
結(jié)合兩個關(guān)鍵概念,你可以看到一個有極大潛能的系統(tǒng)斩披。然而到這里我們只介紹了一個operator:map()溜族,這嚴(yán)重地限制了我們的可能性。在本系列的第二部分垦沉,我們將詳細(xì)介紹RxJava中大量可用的operators煌抒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市厕倍,隨后出現(xiàn)的幾起案子寡壮,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件况既,死亡現(xiàn)場離奇詭異这溅,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)棒仍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門悲靴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人莫其,你說我怎么就攤上這事癞尚。” “怎么了乱陡?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵浇揩,是天一觀的道長。 經(jīng)常有香客問我蛋褥,道長临燃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任烙心,我火速辦了婚禮膜廊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘淫茵。我一直安慰自己爪瓜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布匙瘪。 她就那樣靜靜地躺著铆铆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丹喻。 梳的紋絲不亂的頭發(fā)上薄货,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音碍论,去河邊找鬼谅猾。 笑死,一個胖子當(dāng)著我的面吹牛鳍悠,可吹牛的內(nèi)容都是我干的税娜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼藏研,長吁一口氣:“原來是場噩夢啊……” “哼敬矩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蠢挡,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤弧岳,失蹤者是張志新(化名)和其女友劉穎凳忙,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缩筛,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡消略,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年堡称,在試婚紗的時候發(fā)現(xiàn)自己被綠了瞎抛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡却紧,死狀恐怖桐臊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晓殊,我是刑警寧澤断凶,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站巫俺,受9級特大地震影響认烁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜介汹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一却嗡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嘹承,春花似錦窗价、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骤竹,卻和暖如春帝牡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蒙揣。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工靶溜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸣奔。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓墨技,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挎狸。 傳聞我的和親對象是個殘疾皇子扣汪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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