本系列的七篇文章(目前完成進(jìn)度:2/7):
1撼嗓、什么響應(yīng)式編程
2绑谣、觀(guān)察者模式及Rx基礎(chǔ)概念解釋
3、RxJava深入淺出
4程梦、RxJava+Retrofit 的結(jié)合
5点把、RxJava的高階使用
6、Retrofit的進(jìn)階應(yīng)用
7屿附、總結(jié)
觀(guān)察者模式
其實(shí)非常簡(jiǎn)單郎逃,就是監(jiān)聽(tīng)。我們?cè)谧鯝ndroid開(kāi)發(fā)的時(shí)候挺份,必定繞不過(guò)的就是給Button設(shè)置監(jiān)聽(tīng)器(你沒(méi)做過(guò)算我輸好吧):
bn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("TAG","OnCLick");
}
});
在上面的例子中褒翰,匿名內(nèi)部類(lèi)View.OnClickListener就是觀(guān)察者,按鈕bn就是被觀(guān)察者匀泊。一旦被觀(guān)察者被觀(guān)察到“被點(diǎn)擊”這個(gè)事件影暴,觀(guān)察者就會(huì)做出相應(yīng)的反應(yīng)(出log)。
是的探赫,你已經(jīng)用了很久的觀(guān)察者模式了型宙,只是你不知道而已。
觀(guān)察者模式的描述應(yīng)該是這樣的:定義一個(gè)被觀(guān)察對(duì)象伦吠,再定義一個(gè)(或多個(gè))觀(guān)察者妆兑,觀(guān)察者時(shí)刻在觀(guān)察著被觀(guān)察對(duì)象的一舉一動(dòng),一旦被觀(guān)察對(duì)象的狀態(tài)發(fā)生了變化毛仪,所有的觀(guān)察者都會(huì)馬上做出反應(yīng)搁嗓。
補(bǔ)充一點(diǎn),在現(xiàn)實(shí)的編程中不會(huì)有“時(shí)刻都在觀(guān)察”這種這么蠢的實(shí)現(xiàn)方式的箱靴,所以最好不要將觀(guān)察者模式理解為'觀(guān)察者'時(shí)刻監(jiān)控著'被觀(guān)察者'(雖然語(yǔ)義上這樣的)腺逛,應(yīng)該理解為:'被觀(guān)察者'其實(shí)是‘通知者’,自己有點(diǎn)什么狀態(tài)變化(或者是什么事情發(fā)生)就馬上通知"觀(guān)察者"(就是實(shí)現(xiàn)約定好的)衡怀,然后觀(guān)察者再對(duì)此做出反應(yīng)棍矛。(所以我個(gè)人更傾向于將“觀(guān)察者模式”稱(chēng)為“通知者模式”)
回到本系列的第一篇安疗,那個(gè)和全蛋一起工作的你。流水線(xiàn)模式是觀(guān)察者模式嗎够委,是嗎荐类?是的。但是茁帽,請(qǐng)注意玉罐,只有最后的打包員才是觀(guān)察者!E瞬Α吊输!你們這些加工工人并不算是觀(guān)察者,就算是觀(guān)察者铁追,地位也是比打包員的低的h笛恰(此處我是后來(lái)才意識(shí)到的,因而算是一個(gè)錯(cuò)誤)
為什么這樣說(shuō)脂信,其實(shí)大家可以理解為打包員才是可以睡懶覺(jué)的人癣蟋,當(dāng)然在加工工人沒(méi)事做的時(shí)候也是在睡懶覺(jué)。但是狰闪,就算你們都是在睡懶覺(jué)疯搅,打包員才是“觀(guān)察者”,當(dāng)手機(jī)開(kāi)始在流水線(xiàn)上流動(dòng)的時(shí)候埋泵,流水線(xiàn)只會(huì)通知打包員幔欧,打包員一醒,就會(huì)把你們這些加工工人全部叫醒丽声,幫他干活礁蔗。(需要感受一下打包員和加工工人地位的差距,以及喚醒的時(shí)機(jī))
現(xiàn)在再理一下思路:
- 流水線(xiàn)是“被觀(guān)察者”雁社,
- 流動(dòng)著的手機(jī)是“數(shù)據(jù)流”浴井,
- 打包員是“觀(guān)察者”,
- 觀(guān)察的事件是“手機(jī)毛坯進(jìn)入流水線(xiàn)”霉撵,
- 觀(guān)察者的響應(yīng)動(dòng)作是“將沒(méi)有問(wèn)題的手機(jī)打包好磺浙,有問(wèn)題的手機(jī)另行處理”,
- 訂閱是“事件發(fā)生以后響鈴?fù)ㄖ薄?/li>
- 至于加工工人徒坡,只是在觀(guān)察者的響應(yīng)動(dòng)作發(fā)生前對(duì)數(shù)據(jù)流的處理撕氧。
(Ps,將“被觀(guān)察者”換為“通知者”真的是會(huì)舒服很多)
用RxJava做個(gè)小Demo
例子還是我們的手機(jī)加工喇完,廢話(huà)不多說(shuō)伦泥,直接上代碼:
static void product() {
Phone[] phones = new Phone[5];
for (int i = 0; i < phones.length; i++) {
phones[i] = new Phone(i + "號(hào)iPhone");
}
Observable.from(phones) //Observable,被觀(guān)察者
.map(new Func1<Phone, Phone>() { //裝天線(xiàn)加工
@Override
public Phone call(Phone phone) {
try {
Thread.sleep(2000);
//為了體現(xiàn)順序,我給加工設(shè)置了耗時(shí)
} catch (InterruptedException e) {
e.printStackTrace();
}
return phone.change("裝了天線(xiàn)的");
}
})
.map(new Func1<Phone, Phone>() { //裝攝像頭加工
@Override
public Phone call(Phone phone) {
return phone.change("裝了攝像頭的不脯、");
}
})
.subscribe(new Observer<Phone>() { //Observer府怯,觀(guān)察者訂閱事件
@Override
public void onCompleted() {
System.out.println("搞掂,收工跨新!");
}
@Override
public void onError(Throwable e) {
System.out.println("出了點(diǎn)小問(wèn)題");
}
@Override
public void onNext(Phone phone) {
System.out.println(phone.show()+"被打包好了");
}
});
}
輸出結(jié)果是:
裝了攝像頭的、裝了天線(xiàn)的0號(hào)iPhone被打包好了
裝了攝像頭的坏逢、裝了天線(xiàn)的1號(hào)iPhone被打包好了
裝了攝像頭的域帐、裝了天線(xiàn)的2號(hào)iPhone被打包好了
裝了攝像頭的、裝了天線(xiàn)的3號(hào)iPhone被打包好了
裝了攝像頭的是整、裝了天線(xiàn)的4號(hào)iPhone被打包好了
搞掂肖揣,收工!
Process finished with exit code 0
//附浮入,這里的輸出是每?jī)擅胼敵鲆淮?/p>
RxJava的基本概念
Observable:被觀(guān)察者,也可解釋為發(fā)射源事秀,或數(shù)據(jù)流就是從這里發(fā)射出來(lái)的彤断;
Observer:觀(guān)察者,接收源,數(shù)據(jù)流就是在這里被接收易迹;
Subscriber:“訂閱者”宰衙,也是接收源,那它跟Observer有什么區(qū)別呢睹欲?Subscriber實(shí)現(xiàn)了Observer接口供炼,比Observer多了一個(gè)最重要的方法unsubscribe( ),用來(lái)取消訂閱窘疮,當(dāng)你不再想接收數(shù)據(jù)了袋哼,可以調(diào)用unsubscribe( )方法停止接收,Observer 在 subscribe() 過(guò)程中,最終也會(huì)被轉(zhuǎn)換成 Subscriber 對(duì)象闸衫,一般情況下涛贯,建議使用Subscriber作為接收源;
Subscription:Observable調(diào)用subscribe( )方法返回的對(duì)象蔚出,同樣有unsubscribe( )方法疫蔓,可以用來(lái)取消訂閱事件;
Action0:RxJava中的一個(gè)接口身冬,它只有一個(gè)無(wú)參call()方法衅胀,且無(wú)返回值,同樣還有Action1酥筝,Action2...Action9等滚躯,Action1封裝了含有 1 個(gè)參的call()方法,即call(T t),Action2封裝了含有 2 個(gè)參數(shù)的call方法掸掏,即call(T1 t1茁影,T2 t2),以此類(lèi)推丧凤;
Func0:與Action0非常相似募闲,也有call()方法,但是它是有返回值的愿待,同樣也有Func0浩螺、Func1...Func9;
關(guān)于A(yíng)ction 和 Func:
先說(shuō)Func吧,在上面的demo中仍侥,我們用到了Func1要出,于是我們?nèi)タ纯碏unc1的源碼:
/**
* Represents a function with one argument.
*/
public interface Func1<T, R> extends Function {
R call(T t);
}
非常短吧,只有一個(gè)方法call(T t)农渊,再跑去看看Func2的源碼:
/**
* Represents a function with two arguments.
*/
public interface Func2<T1, T2, R> extends Function {
R call(T1 t1, T2 t2);
}
同樣的患蹂,也是只有一個(gè)方法,call(T1 t1,T2 t2)砸紊。
再看回我們的Demo:
.map(new Func1<Phone, Phone>() { //裝攝像頭加工
@Override
public Phone call(Phone phone) {
return phone.change("裝了攝像頭的传于、");
}
})
在這里,我們想要做的是“給手機(jī)裝攝像頭”醉顽,我們的預(yù)期是我們提供一個(gè)方法給你格了,你只需要把“還沒(méi)裝攝像頭的手機(jī)”當(dāng)作是一個(gè)參數(shù)輸入到這個(gè)方法里面,然后這個(gè)方法就會(huì)返回“裝了攝像頭的手機(jī)”徽鼎。 (很明顯盛末,這段話(huà)的“你”指的是Rx框架)
然而,在Java中是沒(méi)有方法指針這種東西否淤,為了達(dá)到“將指定方法傳遞給Rx框架”的目的悄但,設(shè)計(jì)者設(shè)計(jì)了Func和Action。
我們通過(guò)傳遞Func實(shí)例來(lái)傳遞一個(gè)方法石抡!首先我們先新建一個(gè)Func1實(shí)例檐嚣,然后將我們想要執(zhí)行的方法寫(xiě)到Func1實(shí)例中的call方法中(覆蓋),最后再把這個(gè)實(shí)例傳遞給Rx框架啰扛。Rx框架就會(huì)在指定的時(shí)刻執(zhí)行我們Func1實(shí)例中的call方法嚎京,并把call方法的返回值傳遞到下一個(gè)流程。
大概就是這樣了隐解,通過(guò)傳遞實(shí)例來(lái)傳遞方法在Java中其實(shí)不少見(jiàn)鞍帝,最常見(jiàn)的就是給Button傳遞一個(gè)OnClickListener來(lái)傳遞回調(diào)方法。
此外煞茫,我們?cè)谑褂肍unc有時(shí)可能不止需要一個(gè)輸入帕涌,可能要0個(gè)輸入就好了摄凡,但有時(shí)需要可能需要2個(gè)、3個(gè)...輸入的話(huà)蚓曼,那該如何亲澡。
諸君莫方,我們還有Func0纫版、Func1床绪、Func2...肯定有一款適合你。Func后面的數(shù)字表示的是call方法的參數(shù)個(gè)數(shù)其弊,我們只要根據(jù)需要選擇Func接口就可以了(可看上面的兩個(gè)Func接口的源碼癞己,比較出差別)。
說(shuō)完Func瑞凑,Action也就差不多了末秃,Action和Func的區(qū)別就在于概页,Action沒(méi)有返回值籽御。
最后
似乎也沒(méi)有什么好總結(jié)的了,就這樣了吧惰匙。
Reference:http://www.reibang.com/p/5e93c9101dc5
未經(jīng)授權(quán)技掏,禁止轉(zhuǎn)載
原文地址:http://www.reibang.com/p/b1bd50afb071