Rxjava 從入門到開發(fā)

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

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

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

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

  1. 通過操作符crate對(duì)應(yīng)的Observable0,OnSubscribe0谱姓,Subscriber0借尿。
  2. 通過操作符產(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è)過程何暮。??

  1. Observable1訂閱了Subscriber0奄喂,即調(diào)用OnSubscribe1(OnSubscribeLift)的call方法,開始執(zhí)行邏輯處理海洼;
  2. 在OnSubscribe1的call方法中砍聊,OnSubscribe0調(diào)用call(T t)實(shí)現(xiàn)Observable0和Subscriber1(MapSubscriber)的訂閱,由于調(diào)用了call方法,在例子中實(shí)際上就是調(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)實(shí)現(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)然操作符可不僅這些,但是實(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)度器的種類

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肌幽,一起剝皮案震驚了整個(gè)濱河市晚碾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌喂急,老刑警劉巖格嘁,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異廊移,居然都是意外死亡糕簿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門狡孔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來懂诗,“玉大人,你說我怎么就攤上這事苗膝⊙旰悖” “怎么了?”我有些...
    開封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵辱揭,是天一觀的道長芋类。 經(jīng)常有香客問我,道長界阁,這世上最難降的妖魔是什么侯繁? 我笑而不...
    開封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮泡躯,結(jié)果婚禮上贮竟,老公的妹妹穿的比我還像新娘丽焊。我一直安慰自己,他們只是感情好咕别,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開白布技健。 她就那樣靜靜地躺著,像睡著了一般惰拱。 火紅的嫁衣襯著肌膚如雪雌贱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天偿短,我揣著相機(jī)與錄音欣孤,去河邊找鬼。 笑死昔逗,一個(gè)胖子當(dāng)著我的面吹牛降传,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勾怒,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼婆排,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了笔链?” 一聲冷哼從身側(cè)響起段只,我...
    開封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鉴扫,沒想到半個(gè)月后赞枕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幔妨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年鹦赎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片误堡。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡古话,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锁施,到底是詐尸還是另有隱情陪踩,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布悉抵,位于F島的核電站肩狂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏姥饰。R本人自食惡果不足惜傻谁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望列粪。 院中可真熱鬧审磁,春花似錦谈飒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至钾恢,卻和暖如春手素,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘩蚪。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來泰國打工泉懦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人募舟。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓祠斧,卻偏偏與公主長得像闻察,于是被迫代替她去往敵國和親拱礁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

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