原文鏈接: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煌抒。