rxjava在業(yè)內(nèi)越來越受歡迎,但是雖受歡迎卻難理解,辛虧前面有大神們開路,并把心得體會記錄流傳后輩。比較出名的有拋物線,hi大頭鬼等等,當(dāng)時拜讀大神們的文章,感受頗多,最深的體會是:雖得武林絕學(xué),但無奈本人內(nèi)力不足,學(xué)完秘籍還是覺得似懂非懂,得此招數(shù),卻無法理解精髓。但是經(jīng)過一段時間的摸索,也慢慢有了一些對學(xué)習(xí)rxjava的心得體會,故記錄下來充當(dāng)自己的筆記,也希望可以給想學(xué)習(xí)rxjava的童鞋一些學(xué)習(xí)的思路。本篇文章主要分為兩大部分:(1)如何學(xué)習(xí)rxjava(2)rxjava實戰(zhàn)案例
一、如何學(xué)習(xí)rxjava
如何學(xué)習(xí)rxjava,個人覺得可以從以下幾個方面學(xué)習(xí):
(1)使用rxjava的好處
隨著項目功能增多,代碼量會慢慢增加,復(fù)雜度也會加強,學(xué)習(xí)rxjava可以幫助我們:
- 規(guī)范項目成員的代碼實現(xiàn)
- 讓復(fù)雜的邏輯變得有條理性
通過以上兩點可以規(guī)范團隊的編碼習(xí)慣提高效率,另外也方便我們定位問題,解決問題。在這里多說一句關(guān)于提高效率的問題,如何可以提高編碼效率呢,個人覺得定好編碼規(guī)范,另外寫好代碼架構(gòu)非常重要,目前也出現(xiàn)了一些mvp,flux等等模式幫助我們定好項目規(guī)范。
(2)了解rxjava的基本元素
rxjava的基本三要素如下:
- Observable:被觀察者
- Subscriber:觀察者
- OnSubscribe:一個接口類,是連接被觀察者和觀察者的橋梁,另外要說明的是onSubscribe是Observable的一個局部變量
上面這樣官方的概念其實不容易理解??,如何簡單理解上面這“吉祥三寶”呢,本人是這么理解的,舉個具體例子:造一輛小車,造小車的過程可以分為一道道連續(xù)的工序,簡單來說就是:步驟一(造底盤),步驟二(加上輪子)等等,那么:
Observable:處理步驟一,步驟二等等操作的分別對應(yīng)的“工廠一”,“工廠二”等等。
OnSubscribe:“工廠一”對應(yīng)的“車間一”,“工廠二”對應(yīng)的“車間二”
Subscriber:獲取汽車成品的地方。
我們可以把一個任務(wù)拆分成為一個個依次執(zhí)行的子任務(wù),這種條理性也就是rxjava好用的地方。
(3)rxjava的操作符使用
在(1)(2)節(jié)中我大概介紹了rxjava的概念,讓大家對rxjava有個感性的認(rèn)識,但真正想學(xué)習(xí)rxjava還得從學(xué)習(xí)操作符開始,那么什么是操作符?簡單來說:操作符就是Observable的各種操作,例如:創(chuàng)建,變換,過濾操作等等。在這里需要強調(diào)下的是,Observable通過操作符的操作之后會得到一個新的Observable,每創(chuàng)建一個操作符,簡單來說就是創(chuàng)建了一個子任務(wù),這個在后面源碼分析會講到,這里就不細(xì)講了。
舉個創(chuàng)建操作符Create的代碼例子:代碼結(jié)果很簡單,依次輸出“0,1,2”三個數(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實現(xiàn)了訂閱關(guān)系。
Observable.subscribe(Subscriber<? super T> subscriber)
在例子代碼當(dāng)中Observable通過create操作符創(chuàng)建了一個輸出“0,1,2”這三個數(shù)的容器,也就是說創(chuàng)建了一個Observable對象,在這個容器中真正處理數(shù)據(jù)的是在Observable中的接口變量OnSubscribe的實現(xiàn)方法call(Subscriber<? super Integer> subscriber) 里面,在call方法里面的subscriber其實就是前面說到訂閱Observable的subscriber對象,最后的結(jié)果輸出其實就是回調(diào)Subscriber的onNext(T),onCompleted()和onError(Throwable e)方法(錯誤處理)。
再舉個創(chuàng)建操作符from的代碼例子:代碼結(jié)果很簡單,輸出“1,2,3”三個數(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方法中就會遍歷Iterable或者數(shù)組輸出里面的數(shù)據(jù),仔細(xì)看看subscribe只傳了一個Action1對象,沒有發(fā)現(xiàn)subscriber對象,其實不然,跟蹤方法進去,實際上還是走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呢,因為有時候我們只需要監(jiān)聽Subscriber的onNext(T)方法,不需要onCompleted()和onError(Throwable e)方法,因此可以直接傳Action1對象即可。其實通過傳遞Action也可以實現(xiàn)那三個方法的。。??
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等等,在此不一一說明。
下面再來個相對復(fù)雜的復(fù)雜的例子,也就是變換操作符,變換操作在rxjava非常重要,也是比較常用的操作符。
舉個變換操作符map的具體例子:代碼實現(xiàn)“0,1,2”三個數(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的變換作用。是不是很強大??,在例子中我們看到了一個Func1類,還有Action1,其實還有Actionx和Func1x他們的使用區(qū)別:Funcx處理中間變換過程,封裝有返回值方法,Actionx表示輸出結(jié)果,沒有返回值,常用于代替onNext(T),onCompleted()或者onError(Throwable e)方法,這點上文也有提及。
更多操作符請參考接下來我這系列的文章,敬請期待吧。。。
既然map那么好用,我們不妨深入一點,Map是如何實現(xiàn)“變換”的呢?說明map的變換過程我準(zhǔn)備分為以下幾個步驟說明(ps:說明的代碼為rxjava的1.1.6版本):
- 找到代碼執(zhí)行的起點
- 操作符map與“吉祥三寶”的關(guān)系
- 操作符map如何實現(xiàn)變換操作的過程
1.找到代碼處理邏輯的起點
找到代碼的觸發(fā)點,簡單來說就是找到代碼在哪里開始執(zhí)行的,舉個創(chuàng)建符號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)用一個for循環(huán),但是這里有個疑問,call方法又是在哪里被調(diào)用的呢?我們不妨跟蹤下代碼,看到了Observable通過靜態(tài)create方法創(chuàng)建了一個Observeble對象,Observable對象通過subscribe方法把Subscriber傳了進去,如下:
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);
}
我們暫時忽略hook,在這里我們就可以看到了call方法的調(diào)用,這里便是真正處理邏輯的起點,同時也建立了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操作也涉及了"吉祥三寶",暫時可以用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方法會返回一個新的Observable對象,也就是Observable1,我們再跟蹤lift方法進去。。。。
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)這里有個OnSubscribeLift對象,也就是OnSubscribe1,這里要注意的是在構(gòu)建OnSubscribeLift對象的時候會把onSubscribe0傳進去,也就是OnSubscribe1里面可以調(diào)用OnSubscribe0。現(xiàn)在Observable1和OnSubscribe1都找到了,還剩下Subscriber1,我們回頭看map方法,找到OperatorMap這個類跟蹤進去,發(fā)現(xiàn)里面有個內(nèi)部類MapSubscriber,不用說了,MapSubscriber就是Subscriber1??。至此map涉及的Observable1(map方法返回),OnSubscribe1(OnSubscribeLift),Subscriber1(MapSubscriber)都已經(jīng)全部找到。
至此可以簡單總結(jié)為:Observable0操作通過操作符map產(chǎn)生了新的“吉祥三寶”,即:Observable1,OnSubscribe1,Subscriber1。
3.map操作的具體實現(xiàn)
通過分析map與“吉祥三寶”的關(guān)系,我們得到目前有兩組“吉祥三寶”
- 通過操作符crate對應(yīng)的Observable0,OnSubscribe0,Subscriber0。
- 通過操作符產(chǎn)生的Observable1,OnSubscribe1,Subscriber1。
在此要說明的是Observable0和Subscriber0,還有Observable1和Subscriber1還沒有產(chǎn)生訂閱關(guān)系。
分析map的變換操作,首先我們找到處理代碼邏輯的起點,即:找到OnSubscribe對應(yīng)的call方法,那么call方法對應(yīng)的是OnSubscribe0還是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));
}
});
很明顯是Observable0通過map方法產(chǎn)生的Observable1通過subscribe方法和Subscriber0發(fā)生訂閱關(guān)系,那么第一步執(zhí)行的代碼便是OnSubscribe1的call方法,即:OnSubscribeLift的call方法,我們看下OnSubscribeLift具體對應(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)這句代碼,這句代碼對應(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)對象,st就是Subscriber1,再定位到parent.call(st)這句代碼,這句代碼通過OnSubscribe0的call方法實現(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)用,其實這里的subscriber為subscriber1(MapSubscriber),我們再看看MapSubscriber的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)這句代碼,實現(xiàn)了T到R的轉(zhuǎn)換,在例子代碼中就是Integer到Boolean的轉(zhuǎn)換,這就是map變換的核心,轉(zhuǎn)換后的結(jié)果通過actual.onNext(result)發(fā)送出去了,這個actual是什么東西呢,其實就是Subscriber0,具體可以看看MapSubscriber構(gòu)造函數(shù)傳入,這里就不一一說明了。。最后總結(jié)下整個過程。??
- Observable1訂閱了Subscriber0,即調(diào)用OnSubscribe1(OnSubscribeLift)的call方法,開始執(zhí)行邏輯處理;
- 在OnSubscribe1的call方法中,OnSubscribe0調(diào)用call(T t)實現(xiàn)Observable0和Subscriber1(MapSubscriber)的訂閱,由于調(diào)用了call方法,在例子中實際上就是調(diào)用了for循環(huán);
- for循環(huán)中的調(diào)用了Subscriber1(MapSubscriber)的onNext方法發(fā)送數(shù)據(jù);
- Subscriber1(MapSubscriber)的onNext方法中,通過result = mapper.call(t)實現(xiàn)了操作符map的數(shù)據(jù)轉(zhuǎn)換;
- 最后在Subscriber1(MapSubscriber)在onNext方法中調(diào)用Subscriber0(ActionSubscriber)的onNext方法把結(jié)果回調(diào)到Subscriber0(ActionSubscriber)。
這里再用張圖說下兩組“吉祥三寶”的關(guān)系:
當(dāng)然操作符可不僅這些,但是實現(xiàn)原理都可以參考上面map的分析步驟,找到起點,找到對應(yīng)的“吉祥三寶”,再根據(jù)自己的理解,相信大家都會了解操作符的內(nèi)部原理實現(xiàn),更多的操作符可以操作我操作符系列文章。。
(3)rxjava的線程調(diào)度
除了操作符的使用,線程調(diào)度又是rxjava比較牛逼的功能,線程調(diào)度簡單來說就是指定操作符操作在那個線程任務(wù)執(zhí)行,通過Schedulers類來實現(xià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())方法就可以實現(xiàn)線程調(diào)度功能Schedulers.io()就是一個調(diào)度器,表示指定Create操作在子線程進行,subscribeOn的傳入的調(diào)度器是可變的,具體調(diào)度器的種類和作用可以看以下圖表:
看到以上那么多調(diào)度器,那么rxjava是如何實現(xiàn)線程調(diào)度的呢,其實大家都很容易猜到,那就是利用線程池了。那么如何利用線程池實現(xiàn)的呢?其實還是回到“吉祥三寶”,subscribeOn方法返回的就是一個新的Observable,線程調(diào)度發(fā)生在OnSubscribe的call方法里面,這里就不一一展開了。
除了subscribeOn方法,還有其他方法可以實現(xiàn)線程調(diào)度嗎?答案是有的,通過observeOn方法可以實現(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()其實也是個調(diào)度器,它和上文調(diào)度器不同的地方是,它是特有用于Android上面表示調(diào)度線程在主線程也就是ui線程上面工作,subscribeOn方法上面也可以傳入AndroidSchedulers.mainThread()這個調(diào)度器。subscribeOn和observeOn方法都可以實現(xiàn)線程調(diào)度,那么他們有啥區(qū)別嗎?
(1)subscribeOn方法在整個過程中只需要調(diào)用一次,即便調(diào)用多次也只有第一個subscribeOn方法有效;
(2) subscribeOn會對他前面和后面的操作有效,具體就是對create的call方法和map操作符的call方法產(chǎn)生作用;observeOn只會對后面的操作有效,具體就是把call(Boolean aBoolean)方法指定在ui線程工作;
具體看下拋物線的關(guān)于subscribeOn和observeOn混合使用時的說明圖:
再引用下拋物線大神對上圖的說明。。
圖中共有 5 處含有對事件的操作。由圖中可以看出,①和②兩處受第一個 subscribeOn()影響,運行在紅色線程;③和④處受第一個observeOn()的影響,運行在綠色線程;⑤處受第二個 onserveOn()影響,運行在紫色線程;而第二個 subscribeOn(),由于在通知過程中線程就被第一個 subscribeOn()截斷,因此對整個流程并沒有任何影響。這里也就回答了前面的問題:當(dāng)使用了多個subscribeOn()的時候,只有第一個 subscribeOn()起作用。
最后再總結(jié)一下操作符和和線程調(diào)度。。把一個大功能通過不同的操作符依次分為一道道小工序,工序加工可以通過調(diào)度器指定在什么線程池執(zhí)行,這個過程就是rxjava的作用,這種條理性就是rxjava的魅力所在。
二、rxjava實戰(zhàn)案例
上面說了那么多,還是用一些在Android項目中遇到的案例給大家演示下rxjava的作用吧。
(1)子線程耗時操作,主線程更新ui
項目中非常常見的功能,先貼實現(xiàn)代碼:
Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
Logger.i("執(zhí)行耗時操作....");
try {
Thread.sleep(5000);
subscriber.onNext("耗時操作完成...");
} 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é)果:
實現(xiàn)功能主要通過subscribeOn和observeOn的組合使用,結(jié)果中的紅框處就是對應(yīng)分別的工作線程了。
(2)仿AsnycTack實現(xiàn)
大家應(yīng)該都知道AsyncTask,它可以很容易實現(xiàn)(1),并且它可以更近一步,通過onPreExecute()方法在進行耗時任務(wù)之前可以讓我進行一些準(zhǔn)備操作,例如:顯示個加載中的“菊花”轉(zhuǎn),最后onPostExecute方法中隱藏“菊花”,就類似下圖一樣。
以上功能,通過rxjava又是如何實現(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("耗時操作完成...");
} 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方法就類似實現(xiàn)了AsyncTask的onPostExecute方法,但是需要注意的是:doOnSubscribe受到后面最近的subscribeOn方法影響運行所在的線程,默認(rèn)會運行在subscribe()所在的線程。其他的代碼也相對簡單,大家可以自己看看。
(3)取消訂閱
用rxjava在執(zhí)行任務(wù)的過程當(dāng)中,如果我們想停止的話,那應(yīng)該怎么辦呢?Subscriber訂閱Observable的過程就是處理任務(wù)的過程,那么停止任務(wù)就是取消訂閱操作。如何取消訂閱?在仿AsyncTask的例子也提過,答案就是調(diào)用Subscription的unsubscribe方法,什么是Subscription?可以簡單理解為執(zhí)行任務(wù)的代號,看以下例子就可以很清楚了:
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) {
}
});
很清楚看到整個任務(wù)的處理過程返回的就是一個Subscription??。。在這里再拋出一個問題,為啥要取消訂閱?有人說不是廢話嗎。。前面都已經(jīng)說過了。。停止任務(wù)呀!!!是的,這是其中一個原因。。我根據(jù)自己的理解總結(jié)了下:
- 停止任務(wù)
- 防止內(nèi)存泄露
- 盡可能防止任務(wù)重復(fù)執(zhí)行
第一點:停止任務(wù),這點也是我覺得比AsyncTask好用的地方,因為我們都知道doInbackgroud方法里面的事件停止不好控制,但是rxjava簡單來說把doInbackgroud里面的邏輯分成幾個小邏輯,如果取消訂閱之后,后面的邏輯就不會再執(zhí)行下去了,這也是rxjava比AsyncTask好用的地方。
第二點:防止內(nèi)存泄露理解起來很簡單,rxjava在處理邏輯中可能引用Activity的Context對象,要注意處理。
第三點:在某些情況之下我們想防止任務(wù)重復(fù)執(zhí)行,那么我們通過取消訂閱,停止上一個任務(wù)的執(zhí)行,接著再執(zhí)行新的任務(wù)。
(4)用rxbinding實現(xiàn)過濾輸入
rxbinding :大神JakeWharton的又一杰作,用于防止用戶誤操作,例如:分詞搜索輸入的處理,連續(xù)點擊的處理等等,可以看下圖演示。。
再貼一下關(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));
}
});
代碼比較簡單就不一一多說了,這里需要注意的地方是:假如上圖為頁面一,在頁面一打開一個頁面二,然后關(guān)閉頁面二返回頁面一的時候,需要重新調(diào)用一次代碼才能使得功能正常,所以代碼一般在onStart方法執(zhí)行。
最后再總結(jié)一下:本文通過“學(xué)習(xí)rxjava的好處”引入,和大家分享學(xué)習(xí)rxjava,首先要了解它的三要素,接著就是操作符合和線程調(diào)度學(xué)習(xí),最后通過一些項目案例實戰(zhàn)和大家分享一些使用經(jīng)驗,但是不管怎樣還是得多練習(xí)多打碼,相信你終有收收獲。由于本人水平有限,可能文中存在很多不足,希望大家多多諒解和提出,最后希望本篇文章可以給予那些想學(xué)習(xí)rxjava的小伙伴一些幫助吧??。
附錄:
文章demo