Rxjava 從入門到開發(fā)

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可以幫助我們:

  1. 規(guī)范項目成員的代碼實現(xiàn)
  2. 讓復(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é)果為:

輸出結(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版本):

  1. 找到代碼執(zhí)行的起點
  2. 操作符map與“吉祥三寶”的關(guān)系
  3. 操作符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)系,我們得到目前有兩組“吉祥三寶”

  1. 通過操作符crate對應(yīng)的Observable0,OnSubscribe0,Subscriber0。
  2. 通過操作符產(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é)下整個過程。??

  1. Observable1訂閱了Subscriber0,即調(diào)用OnSubscribe1(OnSubscribeLift)的call方法,開始執(zhí)行邏輯處理;
  2. 在OnSubscribe1的call方法中,OnSubscribe0調(diào)用call(T t)實現(xiàn)Observable0和Subscriber1(MapSubscriber)的訂閱,由于調(diào)用了call方法,在例子中實際上就是調(diào)用了for循環(huán);
  3. for循環(huán)中的調(diào)用了Subscriber1(MapSubscriber)的onNext方法發(fā)送數(shù)據(jù);
  4. Subscriber1(MapSubscriber)的onNext方法中,通過result = mapper.call(t)實現(xiàn)了操作符map的數(shù)據(jù)轉(zhuǎn)換;
  5. 最后在Subscriber1(MapSubscriber)在onNext方法中調(diào)用Subscriber0(ActionSubscriber)的onNext方法把結(jié)果回調(diào)到Subscriber0(ActionSubscriber)。

這里再用張圖說下兩組“吉祥三寶”的關(guān)系:

關(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)度器的種類

看到以上那么多調(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é)了下:

  1. 停止任務(wù)
  2. 防止內(nèi)存泄露
  3. 盡可能防止任務(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

致謝:
給 Android 開發(fā)者的 RxJava 詳解
Awesome-RxJava

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,703評論 2 380

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