rxjava在業(yè)內(nèi)越來越受歡迎,但是雖受歡迎卻難理解创肥,辛虧前面有大神們開路达舒,并把心得體會(huì)記錄流傳后輩值朋。比較出名的有拋物線,hi大頭鬼等等巩搏,當(dāng)時(shí)拜讀大神們的文章昨登,感受頗多,最深的體會(huì)是:雖得武林絕學(xué)贯底,但無奈本人內(nèi)力不足丰辣,學(xué)完秘籍還是覺得似懂非懂,得此招數(shù)禽捆,卻無法理解精髓笙什。但是經(jīng)過一段時(shí)間的摸索,也慢慢有了一些對(duì)學(xué)習(xí)rxjava的心得體會(huì)胚想,故記錄下來充當(dāng)自己的筆記琐凭,也希望可以給想學(xué)習(xí)rxjava的童鞋一些學(xué)習(xí)的思路。本篇文章主要分為兩大部分:(1)如何學(xué)習(xí)rxjava(2)rxjava實(shí)戰(zhàn)案例
一浊服、如何學(xué)習(xí)rxjava
如何學(xué)習(xí)rxjava统屈,個(gè)人覺得可以從以下幾個(gè)方面學(xué)習(xí):
(1)使用rxjava的好處
隨著項(xiàng)目功能增多,代碼量會(huì)慢慢增加臼闻,復(fù)雜度也會(huì)加強(qiáng),學(xué)習(xí)rxjava可以幫助我們:
- 規(guī)范項(xiàng)目成員的代碼實(shí)現(xiàn)
- 讓復(fù)雜的邏輯變得有條理性
通過以上兩點(diǎn)可以規(guī)范團(tuán)隊(duì)的編碼習(xí)慣提高效率囤采,另外也方便我們定位問題述呐,解決問題。在這里多說一句關(guān)于提高效率的問題蕉毯,如何可以提高編碼效率呢乓搬,個(gè)人覺得定好編碼規(guī)范,另外寫好代碼架構(gòu)非常重要代虾,目前也出現(xiàn)了一些mvp进肯,flux等等模式幫助我們定好項(xiàng)目規(guī)范。
(2)了解rxjava的基本元素
rxjava的基本三要素如下:
- Observable:被觀察者
- Subscriber:觀察者
- OnSubscribe:一個(gè)接口類棉磨,是連接被觀察者和觀察者的橋梁江掩,另外要說明的是onSubscribe是Observable的一個(gè)局部變量
上面這樣官方的概念其實(shí)不容易理解??,如何簡(jiǎn)單理解上面這“吉祥三寶”呢乘瓤,本人是這么理解的环形,舉個(gè)具體例子:造一輛小車,造小車的過程可以分為一道道連續(xù)的工序衙傀,簡(jiǎn)單來說就是:步驟一(造底盤)抬吟,步驟二(加上輪子)等等,那么:
Observable:處理步驟一统抬,步驟二等等操作的分別對(duì)應(yīng)的“工廠一”火本,“工廠二”等等危队。
OnSubscribe:“工廠一”對(duì)應(yīng)的“車間一”,“工廠二”對(duì)應(yīng)的“車間二”
Subscriber:獲取汽車成品的地方钙畔。
我們可以把一個(gè)任務(wù)拆分成為一個(gè)個(gè)依次執(zhí)行的子任務(wù)茫陆,這種條理性也就是rxjava好用的地方。
(3)rxjava的操作符使用
在(1)(2)節(jié)中我大概介紹了rxjava的概念刃鳄,讓大家對(duì)rxjava有個(gè)感性的認(rèn)識(shí)盅弛,但真正想學(xué)習(xí)rxjava還得從學(xué)習(xí)操作符開始,那么什么是操作符叔锐?簡(jiǎn)單來說:操作符就是Observable的各種操作挪鹏,例如:創(chuàng)建,變換愉烙,過濾操作等等讨盒。在這里需要強(qiáng)調(diào)下的是,Observable通過操作符的操作之后會(huì)得到一個(gè)新的Observable步责,每創(chuàng)建一個(gè)操作符返顺,簡(jiǎn)單來說就是創(chuàng)建了一個(gè)子任務(wù),這個(gè)在后面源碼分析會(huì)講到蔓肯,這里就不細(xì)講了遂鹊。
舉個(gè)創(chuàng)建操作符Create的代碼例子:代碼結(jié)果很簡(jiǎn)單,依次輸出“0蔗包,1秉扑,2”三個(gè)數(shù),最后輸出“hello rxjava execute complete”這句話。
Observable.create(new Observable.OnSubscribe<Integer>(){
@Override
public void call(Subscriber<? super Integer> subscriber) {
for(int i=0;i<3;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
Log.i(Log.TAG,"hello rxjava execute complete");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
Log.i(Log.TAG,""+integer);
}
});
Observable 通過subscribe方法和Subscriber實(shí)現(xiàn)了訂閱關(guān)系调限。
Observable.subscribe(Subscriber<? super T> subscriber)
在例子代碼當(dāng)中Observable通過create操作符創(chuàng)建了一個(gè)輸出“0舟陆,1,2”這三個(gè)數(shù)的容器耻矮,也就是說創(chuàng)建了一個(gè)Observable對(duì)象秦躯,在這個(gè)容器中真正處理數(shù)據(jù)的是在Observable中的接口變量OnSubscribe的實(shí)現(xiàn)方法call(Subscriber<? super Integer> subscriber) 里面,在call方法里面的subscriber其實(shí)就是前面說到訂閱Observable的subscriber對(duì)象裆装,最后的結(jié)果輸出其實(shí)就是回調(diào)Subscriber的onNext(T),onCompleted()和onError(Throwable e)方法(錯(cuò)誤處理)踱承。
再舉個(gè)創(chuàng)建操作符from的代碼例子:代碼結(jié)果很簡(jiǎn)單,輸出“1哨免,2勾扭,3”三個(gè)數(shù)
Integer[] item={1,2,3};
Observable.from(item).subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.i(Log.TAG, "" + integer);
}
});
from需要傳入是Iterable或者數(shù)組,然后在call方法中就會(huì)遍歷Iterable或者數(shù)組輸出里面的數(shù)據(jù)铁瞒,仔細(xì)看看subscribe只傳了一個(gè)Action1對(duì)象妙色,沒有發(fā)現(xiàn)subscriber對(duì)象,其實(shí)不然慧耍,跟蹤方法進(jìn)去身辨,實(shí)際上還是走Observable被Subscriber訂閱的套路丐谋,只不過這里的是ActionSubscriber。
public final Subscription subscribe(final Action1<? super T> onNext) {
if (onNext == null) {
throw new IllegalArgumentException("onNext can not be null");
}
Action1<Throwable> onError = InternalObservableUtils.ERROR_NOT_IMPLEMENTED;
Action0 onCompleted = Actions.empty();
return subscribe(new ActionSubscriber<T>(onNext, onError, onCompleted));
}
那么為啥這里傳的是Action1呢煌珊,因?yàn)橛袝r(shí)候我們只需要監(jiān)聽Subscriber的onNext(T)方法号俐,不需要onCompleted()和onError(Throwable e)方法,因此可以直接傳Action1對(duì)象即可定庵。其實(shí)通過傳遞Action也可以實(shí)現(xiàn)那三個(gè)方法的吏饿。。??
Integer[] item={1,2,3};
Observable.from(item).subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.i(Log.TAG, "form " + integer);
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Log.e(Log.TAG, "error: "+throwable.getMessage());
}
}, new Action0() {
@Override
public void call() {
Log.i(Log.TAG,"from complete");
}
});
創(chuàng)建操作符還有Interval蔬浙,Range等等猪落,在此不一一說明。
下面再來個(gè)相對(duì)復(fù)雜的復(fù)雜的例子畴博,也就是變換操作符笨忌,變換操作在rxjava非常重要,也是比較常用的操作符俱病。
舉個(gè)變換操作符map的具體例子:代碼實(shí)現(xiàn)“0,1,2”三個(gè)數(shù)官疲,如果是偶數(shù)就輸出為true,否則為false亮隙。
Observable.create(new Observable.OnSubscribe<Integer>(){
@Override
public void call(Subscriber<? super Integer> subscriber) {
for(int i=0;i<3;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}).map(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer integer) {
return (integer%2)==0;
}
}).subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean aBoolean) {
Log.i("map",String.valueOf(aBoolean));
}
});
代碼的輸出結(jié)果為:
從上面的例子看出途凫,map的作用就是把Integer的數(shù)字轉(zhuǎn)成了Boolean,這就是map的變換作用溢吻。是不是很強(qiáng)大??维费,在例子中我們看到了一個(gè)Func1類,還有Action1煤裙,其實(shí)還有Actionx和Func1x他們的使用區(qū)別:Funcx處理中間變換過程掩完,封裝有返回值方法噪漾,Actionx表示輸出結(jié)果硼砰,沒有返回值,常用于代替onNext(T),onCompleted()或者onError(Throwable e)方法欣硼,這點(diǎn)上文也有提及题翰。
更多操作符請(qǐng)參考接下來我這系列的文章,敬請(qǐng)期待吧诈胜。豹障。。
既然map那么好用焦匈,我們不妨深入一點(diǎn)血公,Map是如何實(shí)現(xiàn)“變換”的呢?說明map的變換過程我準(zhǔn)備分為以下幾個(gè)步驟說明(ps:說明的代碼為rxjava的1.1.6版本):
- 找到代碼執(zhí)行的起點(diǎn)
- 操作符map與“吉祥三寶”的關(guān)系
- 操作符map如何實(shí)現(xiàn)變換操作的過程
1.找到代碼處理邏輯的起點(diǎn)
找到代碼的觸發(fā)點(diǎn)缓熟,簡(jiǎn)單來說就是找到代碼在哪里開始執(zhí)行的累魔,舉個(gè)創(chuàng)建符號(hào)Create的例子:
Observable.create(new Observable.OnSubscribe<Integer>(){
@Override
public void call(Subscriber<? super Integer> subscriber) {
for(int i=0;i<3;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
Log.i(Log.TAG,"hello rxjava execute complete");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
Log.i(Log.TAG,""+integer);
}
});
我們都知道最開始是在call(Subscriber<? super Integer> subscriber)調(diào)用一個(gè)for循環(huán)摔笤,但是這里有個(gè)疑問,call方法又是在哪里被調(diào)用的呢垦写?我們不妨跟蹤下代碼吕世,看到了Observable通過靜態(tài)create方法創(chuàng)建了一個(gè)Observeble對(duì)象,Observable對(duì)象通過subscribe方法把Subscriber傳了進(jìn)去梯投,如下:
public final Subscription subscribe(Subscriber<? super T> subscriber) {
return Observable.subscribe(subscriber, this);
}
我們繼續(xù)跟蹤代碼Observable.subscribe(subscriber, this)方法命辖。。分蓖。
static <T> Subscription subscribe(Subscriber<? super T> subscriber, Observable<T> observable) {
//省略其他代碼尔艇,只保留核心說明部分
// allow the hook to intercept and/or decorate
hook.onSubscribeStart(observable, observable.onSubscribe).call(subscriber);
return hook.onSubscribeReturn(subscriber);
}
我們暫時(shí)忽略hook,在這里我們就可以看到了call方法的調(diào)用咆疗,這里便是真正處理邏輯的起點(diǎn)漓帚,同時(shí)也建立了Observable和和Subscriber的訂閱關(guān)系。例子大概過程是:代碼調(diào)用了onSubscribe的call方法-->執(zhí)行for循環(huán)-->通過call(Subscriber<? super Integer> subscriber)傳入的subscriber發(fā)送結(jié)果-->Subscriber的onNext等方法中訂閱想要的結(jié)果午磁。
2.操作符map與“吉祥三寶”的關(guān)系
根據(jù)上面案例尝抖,我們發(fā)現(xiàn)通過create操作符完成一次操作就涉及到”吉祥三寶“調(diào)用一次了,姑且用Observable0迅皇,Subscriber0昧辽,OnSubscribe0表示,那么通過create和map一起調(diào)用的話和”吉祥三寶“的聯(lián)系又是怎樣的呢登颓,答案是map操作也涉及了"吉祥三寶"搅荞,暫時(shí)可以用Observable1,Subscriber1框咙,OnSubscribe1來表示咕痛。
Observable.create(new Observable.OnSubscribe<Integer>(){
@Override
public void call(Subscriber<? super Integer> subscriber) {
for(int i=0;i<3;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}).map(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer integer) {
return (integer%2)==0;
}
}).subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean aBoolean) {
Log.i("map",String.valueOf(aBoolean));
}
});
在代碼中很容易看到Observable調(diào)用map方法會(huì)返回一個(gè)新的Observable對(duì)象,也就是Observable1喇嘱,我們?cè)俑檒ift方法進(jìn)去茉贡。。者铜。腔丧。
public final <R> Observable<R> map(Func1<? super T, ? extends R> func) {
return lift(new OperatorMap<T, R>(func));
}
public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) {
return new Observable<R>(new OnSubscribeLift<T, R>(onSubscribe, operator));
}
我們發(fā)現(xiàn)這里有個(gè)OnSubscribeLift對(duì)象,也就是OnSubscribe1作烟,這里要注意的是在構(gòu)建OnSubscribeLift對(duì)象的時(shí)候會(huì)把onSubscribe0傳進(jìn)去愉粤,也就是OnSubscribe1里面可以調(diào)用OnSubscribe0。現(xiàn)在Observable1和OnSubscribe1都找到了拿撩,還剩下Subscriber1衣厘,我們回頭看map方法表谊,找到OperatorMap這個(gè)類跟蹤進(jìn)去娇斩,發(fā)現(xiàn)里面有個(gè)內(nèi)部類MapSubscriber魏烫,不用說了沐鼠,MapSubscriber就是Subscriber1??。至此map涉及的Observable1(map方法返回)坤检,OnSubscribe1(OnSubscribeLift)兴猩,Subscriber1(MapSubscriber)都已經(jīng)全部找到。
至此可以簡(jiǎn)單總結(jié)為:Observable0操作通過操作符map產(chǎn)生了新的“吉祥三寶”早歇,即:Observable1倾芝,OnSubscribe1,Subscriber1箭跳。
3.map操作的具體實(shí)現(xiàn)
通過分析map與“吉祥三寶”的關(guān)系晨另,我們得到目前有兩組“吉祥三寶”
- 通過操作符crate對(duì)應(yīng)的Observable0,OnSubscribe0谱姓,Subscriber0借尿。
- 通過操作符產(chǎn)生的Observable1,OnSubscribe1屉来,Subscriber1路翻。
在此要說明的是Observable0和Subscriber0,還有Observable1和Subscriber1還沒有產(chǎn)生訂閱關(guān)系茄靠。
分析map的變換操作茂契,首先我們找到處理代碼邏輯的起點(diǎn),即:找到OnSubscribe對(duì)應(yīng)的call方法慨绳,那么call方法對(duì)應(yīng)的是OnSubscribe0還是OnSubscribe1呢掉冶?我們?cè)倏匆淮卫哟a。脐雪。
Observable.create(new Observable.OnSubscribe<Integer>(){
@Override
public void call(Subscriber<? super Integer> subscriber) {
for(int i=0;i<3;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}).map(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer integer) {
return (integer%2)==0;
}
}).subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean aBoolean) {
Log.i("map",String.valueOf(aBoolean));
}
});
很明顯是Observable0通過map方法產(chǎn)生的Observable1通過subscribe方法和Subscriber0發(fā)生訂閱關(guān)系厌小,那么第一步執(zhí)行的代碼便是OnSubscribe1的call方法,即:OnSubscribeLift的call方法战秋,我們看下OnSubscribeLift具體對(duì)應(yīng)的代碼:
public final class OnSubscribeLift<T, R> implements OnSubscribe<R> {
static final RxJavaObservableExecutionHook hook = RxJavaPlugins.getInstance().getObservableExecutionHook();
final OnSubscribe<T> parent;
final Operator<? extends R, ? super T> operator;
public OnSubscribeLift(OnSubscribe<T> parent, Operator<? extends R, ? super T> operator) {
this.parent = parent;
this.operator = operator;
}
@Override
public void call(Subscriber<? super R> o) {
//只保留關(guān)鍵代碼
Subscriber<? super T> st = hook.onLift(operator).call(o);
parent.call(st);
}
}
在OnSubscribeLift的call方法里面璧亚,我們先定位hook.onLift(operator).call(o)這句代碼,這句代碼對(duì)應(yīng)的是OperatorMap的call方法获询,具體如下:
public Subscriber<? super T> call(final Subscriber<? super R> o) {
MapSubscriber<T, R> parent = new MapSubscriber<T, R>(o, transformer);
o.add(parent);
return parent;
}
明顯看到該方法返回了Subscriber1(MapSubscriber)對(duì)象涨岁,st就是Subscriber1拐袜,再定位到parent.call(st)這句代碼吉嚣,這句代碼通過OnSubscribe0的call方法實(shí)現(xiàn)了Observable0和Subscriber1(MapSubscriber)的訂閱,繼續(xù)順著parent.call(st)方法蹬铺,即:OnSubscribe0的call方法尝哆,我們終于回到了for循環(huán)。甜攀。??
Observable.create(new Observable.OnSubscribe<Integer>(){
@Override
public void call(Subscriber<? super Integer> subscriber) {
for(int i=0;i<3;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
})
在for循環(huán)中subscriber.onNext(i)的調(diào)用秋泄,其實(shí)這里的subscriber為subscriber1(MapSubscriber)琐馆,我們?cè)倏纯碝apSubscriber的onNext方法。恒序。瘦麸。
static final class MapSubscriber<T, R> extends Subscriber<T> {
final Subscriber<? super R> actual;
final Func1<? super T, ? extends R> mapper;
public MapSubscriber(Subscriber<? super R> actual, Func1<? super T, ? extends R> mapper) {
this.actual = actual;
this.mapper = mapper;
}
//只保留需要說明的核心代碼
@Override
public void onNext(T t) {
R result;
result = mapper.call(t);
actual.onNext(result);
}
}
從上面代碼看到通過result = mapper.call(t)這句代碼,實(shí)現(xiàn)了T到R的轉(zhuǎn)換歧胁,在例子代碼中就是Integer到Boolean的轉(zhuǎn)換滋饲,這就是map變換的核心,轉(zhuǎn)換后的結(jié)果通過actual.onNext(result)發(fā)送出去了喊巍,這個(gè)actual是什么東西呢屠缭,其實(shí)就是Subscriber0,具體可以看看MapSubscriber構(gòu)造函數(shù)傳入崭参,這里就不一一說明了呵曹。。最后總結(jié)下整個(gè)過程何暮。??
- Observable1訂閱了Subscriber0奄喂,即調(diào)用OnSubscribe1(OnSubscribeLift)的call方法,開始執(zhí)行邏輯處理海洼;
- 在OnSubscribe1的call方法中砍聊,OnSubscribe0調(diào)用call(T t)實(shí)現(xiàn)Observable0和Subscriber1(MapSubscriber)的訂閱,由于調(diào)用了call方法,在例子中實(shí)際上就是調(diào)用了for循環(huán)贰军;
- for循環(huán)中的調(diào)用了Subscriber1(MapSubscriber)的onNext方法發(fā)送數(shù)據(jù)玻蝌;
- Subscriber1(MapSubscriber)的onNext方法中,通過result = mapper.call(t)實(shí)現(xiàn)了操作符map的數(shù)據(jù)轉(zhuǎn)換词疼;
- 最后在Subscriber1(MapSubscriber)在onNext方法中調(diào)用Subscriber0(ActionSubscriber)的onNext方法把結(jié)果回調(diào)到Subscriber0(ActionSubscriber)俯树。
這里再用張圖說下兩組“吉祥三寶”的關(guān)系:
當(dāng)然操作符可不僅這些,但是實(shí)現(xiàn)原理都可以參考上面map的分析步驟贰盗,找到起點(diǎn)许饿,找到對(duì)應(yīng)的“吉祥三寶”,再根據(jù)自己的理解舵盈,相信大家都會(huì)了解操作符的內(nèi)部原理實(shí)現(xiàn)陋率,更多的操作符可以操作我操作符系列文章。秽晚。
(3)rxjava的線程調(diào)度
除了操作符的使用瓦糟,線程調(diào)度又是rxjava比較牛逼的功能,線程調(diào)度簡(jiǎn)單來說就是指定操作符操作在那個(gè)線程任務(wù)執(zhí)行赴蝇,通過Schedulers類來實(shí)現(xiàn)菩浙,先看個(gè)簡(jiǎn)單的例子:
Observable.create(new Observable.OnSubscribe<Integer>(){
@Override
public void call(Subscriber<? super Integer> subscriber) {
for(int i=0;i<3;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.io()).subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
Log.i("test",String.valueOf(integer));
}
});
例子中在操作符Create后面加上subscribeOn(Schedulers.io())方法就可以實(shí)現(xiàn)線程調(diào)度功能Schedulers.io()就是一個(gè)調(diào)度器,表示指定Create操作在子線程進(jìn)行,subscribeOn的傳入的調(diào)度器是可變的劲蜻,具體調(diào)度器的種類和作用可以看以下圖表:
看到以上那么多調(diào)度器陆淀,那么rxjava是如何實(shí)現(xiàn)線程調(diào)度的呢,其實(shí)大家都很容易猜到先嬉,那就是利用線程池了轧苫。那么如何利用線程池實(shí)現(xiàn)的呢?其實(shí)還是回到“吉祥三寶”疫蔓,subscribeOn方法返回的就是一個(gè)新的Observable浸剩,線程調(diào)度發(fā)生在OnSubscribe的call方法里面,這里就不一一展開了鳄袍。
除了subscribeOn方法绢要,還有其他方法可以實(shí)現(xiàn)線程調(diào)度嗎?答案是有的拗小,通過observeOn方法可以實(shí)現(xiàn)線程調(diào)度重罪,還看是例子吧。哀九。
Observable.create(new Observable.OnSubscribe<Integer>(){
@Override
public void call(Subscriber<? super Integer> subscriber) {
for(int i=0;i<3;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}).subscribeOn(Schedulers.io()).map(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer integer) {
return (integer%2)==0;
}
}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean aBoolean) {
Log.i("map",String.valueOf(aBoolean));
}
});
我們定位到observeOn(AndroidSchedulers.mainThread())這句代碼剿配,先介紹下AndroidSchedulers.mainThread(),AndroidSchedulers.mainThread()其實(shí)也是個(gè)調(diào)度器阅束,它和上文調(diào)度器不同的地方是呼胚,它是特有用于Android上面表示調(diào)度線程在主線程也就是ui線程上面工作,subscribeOn方法上面也可以傳入AndroidSchedulers.mainThread()這個(gè)調(diào)度器息裸。subscribeOn和observeOn方法都可以實(shí)現(xiàn)線程調(diào)度蝇更,那么他們有啥區(qū)別嗎?
(1)subscribeOn方法在整個(gè)過程中只需要調(diào)用一次呼盆,即便調(diào)用多次也只有第一個(gè)subscribeOn方法有效;
(2) subscribeOn會(huì)對(duì)他前面和后面的操作有效年扩,具體就是對(duì)create的call方法和map操作符的call方法產(chǎn)生作用;observeOn只會(huì)對(duì)后面的操作有效访圃,具體就是把call(Boolean aBoolean)方法指定在ui線程工作;
具體看下拋物線的關(guān)于subscribeOn和observeOn混合使用時(shí)的說明圖:
再引用下拋物線大神對(duì)上圖的說明厨幻。。
圖中共有 5 處含有對(duì)事件的操作腿时。由圖中可以看出况脆,①和②兩處受第一個(gè) subscribeOn()影響,運(yùn)行在紅色線程批糟;③和④處受第一個(gè)observeOn()的影響格了,運(yùn)行在綠色線程;⑤處受第二個(gè) onserveOn()影響跃赚,運(yùn)行在紫色線程笆搓;而第二個(gè) subscribeOn(),由于在通知過程中線程就被第一個(gè) subscribeOn()截?cái)辔嘲粒虼藢?duì)整個(gè)流程并沒有任何影響满败。這里也就回答了前面的問題:當(dāng)使用了多個(gè)subscribeOn()的時(shí)候,只有第一個(gè) subscribeOn()起作用叹括。
最后再總結(jié)一下操作符和和線程調(diào)度算墨。。把一個(gè)大功能通過不同的操作符依次分為一道道小工序汁雷,工序加工可以通過調(diào)度器指定在什么線程池執(zhí)行净嘀,這個(gè)過程就是rxjava的作用,這種條理性就是rxjava的魅力所在侠讯。
二挖藏、rxjava實(shí)戰(zhàn)案例
上面說了那么多,還是用一些在Android項(xiàng)目中遇到的案例給大家演示下rxjava的作用吧厢漩。
(1)子線程耗時(shí)操作膜眠,主線程更新ui
項(xiàng)目中非常常見的功能,先貼實(shí)現(xiàn)代碼:
Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
Logger.i("執(zhí)行耗時(shí)操作....");
try {
Thread.sleep(5000);
subscriber.onNext("耗時(shí)操作完成...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<String>() {
@Override
public void call(String s) {
Logger.i(s);
}
});
再貼輸出結(jié)果:
實(shí)現(xiàn)功能主要通過subscribeOn和observeOn的組合使用溜嗜,結(jié)果中的紅框處就是對(duì)應(yīng)分別的工作線程了宵膨。
(2)仿AsnycTack實(shí)現(xiàn)
大家應(yīng)該都知道AsyncTask,它可以很容易實(shí)現(xiàn)(1)炸宵,并且它可以更近一步辟躏,通過onPreExecute()方法在進(jìn)行耗時(shí)任務(wù)之前可以讓我進(jìn)行一些準(zhǔn)備操作,例如:顯示個(gè)加載中的“菊花”轉(zhuǎn)土全,最后onPostExecute方法中隱藏“菊花”捎琐,就類似下圖一樣。
以上功能裹匙,通過rxjava又是如何實(shí)現(xiàn)的呢野哭?繼續(xù)貼代碼。幻件。
@OnClick(R.id.btn2) void onButton2Click(){
unBindSubscription();
subscription= Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
Logger.i("doInBackground()....");
try {
Thread.sleep(5000);
subscriber.onNext("耗時(shí)操作完成...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).subscribeOn(Schedulers.io()).doOnSubscribe(new Action0() {
@Override
public void call() {
progressBar.setVisibility(View.VISIBLE);
Logger.i("onPreExecute() ....");
}
}).subscribeOn(AndroidSchedulers.mainThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<String>() {
@Override
public void call(String s) {
Logger.i("onPostExecute .... ".concat(s));
progressBar.setVisibility(View.GONE);
}
});
}
doOnSubscribe方法就類似實(shí)現(xiàn)了AsyncTask的onPostExecute方法拨黔,但是需要注意的是:doOnSubscribe受到后面最近的subscribeOn方法影響運(yùn)行所在的線程,默認(rèn)會(huì)運(yùn)行在subscribe()所在的線程绰沥。其他的代碼也相對(duì)簡(jiǎn)單篱蝇,大家可以自己看看。
(3)取消訂閱
用rxjava在執(zhí)行任務(wù)的過程當(dāng)中徽曲,如果我們想停止的話零截,那應(yīng)該怎么辦呢?Subscriber訂閱Observable的過程就是處理任務(wù)的過程秃臣,那么停止任務(wù)就是取消訂閱操作涧衙。如何取消訂閱哪工?在仿AsyncTask的例子也提過,答案就是調(diào)用Subscription的unsubscribe方法弧哎,什么是Subscription雁比?可以簡(jiǎn)單理解為執(zhí)行任務(wù)的代號(hào),看以下例子就可以很清楚了:
Subscription subscription=Observable.create(new Observable.OnSubscribe<Integer>(){
@Override
public void call(Subscriber<? super Integer> subscriber) {
for(int i=0;i<3;i++){
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}).subscribe(new Action1<Integer>() {
@Override
public void call(Integer integer) {
}
});
很清楚看到整個(gè)任務(wù)的處理過程返回的就是一個(gè)Subscription??撤嫩。偎捎。在這里再拋出一個(gè)問題,為啥要取消訂閱序攘?有人說不是廢話嗎茴她。。前面都已經(jīng)說過了程奠。丈牢。停止任務(wù)呀!C樯场赡麦!是的,這是其中一個(gè)原因帕识。泛粹。我根據(jù)自己的理解總結(jié)了下:
- 停止任務(wù)
- 防止內(nèi)存泄露
- 盡可能防止任務(wù)重復(fù)執(zhí)行
第一點(diǎn):停止任務(wù),這點(diǎn)也是我覺得比AsyncTask好用的地方肮疗,因?yàn)槲覀兌贾纃oInbackgroud方法里面的事件停止不好控制晶姊,但是rxjava簡(jiǎn)單來說把doInbackgroud里面的邏輯分成幾個(gè)小邏輯,如果取消訂閱之后伪货,后面的邏輯就不會(huì)再執(zhí)行下去了们衙,這也是rxjava比AsyncTask好用的地方。
第二點(diǎn):防止內(nèi)存泄露理解起來很簡(jiǎn)單碱呼,rxjava在處理邏輯中可能引用Activity的Context對(duì)象蒙挑,要注意處理。
第三點(diǎn):在某些情況之下我們想防止任務(wù)重復(fù)執(zhí)行愚臀,那么我們通過取消訂閱忆蚀,停止上一個(gè)任務(wù)的執(zhí)行,接著再執(zhí)行新的任務(wù)姑裂。
(4)用rxbinding實(shí)現(xiàn)過濾輸入
rxbinding :大神JakeWharton的又一杰作馋袜,用于防止用戶誤操作,例如:分詞搜索輸入的處理舶斧,連續(xù)點(diǎn)擊的處理等等欣鳖,可以看下圖演示。茴厉。
再貼一下關(guān)鍵代碼:
RxTextView.textChanges(editText).debounce(500, TimeUnit.MILLISECONDS).observeOn(Schedulers.io()).map(new Func1<CharSequence, List<String>>() {
@Override
public List<String> call(CharSequence charSequence) {
try {
if(TextUtils.isEmpty(charSequence)){
return null;
}
List<String> stringList =new ArrayList<String>();
String key=charSequence.toString();
for (String num: baseDatas){
if(num.contains(key)){
stringList.add(num);
}
}
return stringList;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<List<String>>() {
@Override
public void call(List<String> list) {
updateData(list);
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Logger.e(Log.getStackTraceString(throwable));
}
});
代碼比較簡(jiǎn)單就不一一多說了泽台,這里需要注意的地方是:假如上圖為頁面一什荣,在頁面一打開一個(gè)頁面二,然后關(guān)閉頁面二返回頁面一的時(shí)候怀酷,需要重新調(diào)用一次代碼才能使得功能正常稻爬,所以代碼一般在onStart方法執(zhí)行。
最后再總結(jié)一下:本文通過“學(xué)習(xí)rxjava的好處”引入胰坟,和大家分享學(xué)習(xí)rxjava因篇,首先要了解它的三要素泞辐,接著就是操作符合和線程調(diào)度學(xué)習(xí)笔横,最后通過一些項(xiàng)目案例實(shí)戰(zhàn)和大家分享一些使用經(jīng)驗(yàn),但是不管怎樣還是得多練習(xí)多打碼咐吼,相信你終有收收獲吹缔。由于本人水平有限,可能文中存在很多不足锯茄,希望大家多多諒解和提出厢塘,最后希望本篇文章可以給予那些想學(xué)習(xí)rxjava的小伙伴一些幫助吧??。
附錄:
文章demo