RxAndroid 基礎(chǔ)

RxAndroid 基礎(chǔ)#

參考hi大頭鬼hi 的微博沐飘,寫代碼進(jìn)行測試學(xué)習(xí),以下記錄共享,同時以便之后查閱影晓。 由于不熟悉lambda疤祭,同時開始學(xué)習(xí)也不建議直接使用lambda,以下大部分代碼均使用常規(guī)方法編寫蜈项。

RxJava在github上的地址
RxAndroid在github上的地址

首先侥衬,工程中引入:

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'

//引入RxAndroid----begin
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
//引入RxAndroid----end

// RxBinding
compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.4.0'
compile 'com.jakewharton.rxbinding:rxbinding-design:0.4.0'
}

一怀樟、基本使用

常規(guī)語法

Observable<String> myObservable = Observable.create(
            new Observable.OnSubscribe<String>() {
                @Override
                public void call(Subscriber<? super String> sub) {
                    sub.onNext("Observable Hello, world!");
                    sub.onCompleted();
                }
            }
    );
    
    Subscriber<String> mySubscriber = new Subscriber<String>() {
        @Override
        public void onNext(String s) {
            L.d("onNext s: " + s);
           }

        @Override
        public void onCompleted() { }

        @Override
        public void onError(Throwable e) { }
    };

    myObservable.subscribe(mySubscriber);

簡化語法

Observable.just(1)
            .subscribe(new Action1<Integer>() {
                @Override
                public void call(Integer integer) {
                    L.d("onNext integer: " + integer);
                }
            });

map操作符變換

Observable.just("Hello, world!")
            .map(new Func1<String, String>() {
                @Override
                public String call(String s) {
                    return s + " -Map";
                }
            })
            .subscribe(new Action1<String>() {
                @Override
                public void call(String s) {
                    L.d("onNext s: " + s);
                }
            });

二虑灰、操作符

Observable.from()

Observable.from()方法,它接收一個集合作為輸入对湃,然后每次輸出一個元素給subscriber:

 List<String> urls = new ArrayList<>();
    urls.add("url1");
    urls.add("url2");
    urls.add("url3");
    Observable.from(urls)
            .subscribe(new Action1<String>() {
                @Override
                public void call(String s) {
                    L.d("onNext s: " + s);
                }
            });

輸出如下:
nNext s: url1
onNext s: url2
onNext s: url3

   Observable<List<String>> myObservableUrls = Observable.create(
            new Observable.OnSubscribe<List<String>>() {
                @Override
                public void call(Subscriber<? super List<String>> subscriber) {
                    List<String> urls = new ArrayList<>();
                    urls.add("url1");
                    urls.add("url2");
                    urls.add("url3");
                    subscriber.onNext(urls);
                }
            }
    );

    myObservableUrls.subscribe(new Action1<List<String>>() {
        @Override
        public void call(List<String> strings) {
            Observable.from(strings)
                    .subscribe(new Action1<String>() {
                        @Override
                        public void call(String s) {
                            L.d("myObservableUrls onNext s: " + s);
                        }
                    });
        }
    });

輸出如下:
myObservableUrls onNext s: url1
myObservableUrls onNext s: url2
myObservableUrls onNext s: url3
在前一個Observable輸出是list時斤儿,嵌套太多,看起來比較亂陕贮。

Observable.flatMap()

Observable.flatMap()接收一個Observable的輸出作為輸入,同時輸出另外一個Observable戈擒。

 myObservableUrls.flatMap(new Func1<List<String>, Observable<String>>() {
        @Override
        public Observable<String> call(List<String> urls) {
            return Observable.from(urls);
        }
    }).subscribe(new Action1<String>() {
        @Override
        public void call(String s) {
            L.d("myObservableUrls.flatMap onNext s: " + s);
        }
    }); 

避免了上面直接使用Observable.from時需要內(nèi)如再次subscribe導(dǎo)致的內(nèi)部嵌套。理解flatMap的關(guān)鍵點在于,flatMap輸出的新的Observable正是我們在Subscriber想要接收的“绯瑁現(xiàn)在Subscriber不再收到List<String>,而是收到一些列單個的字符串轿偎,就像Observable.from()的輸出一樣。

flatMap(),它可以返回任何它想返回的Observable對象仓蛆。
接著前面的例子豆拨,現(xiàn)在我不想打印URL了,而是要打印收到的每個網(wǎng)站的標(biāo)題弥搞。

現(xiàn)在的方法每次只能傳入一個URL,并且返回值不是一個String,而是一個輸出String的Observabl對象:

  private Observable<String> getTitle(final String url) {
    return Observable.create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
            subscriber.onNext("title: " + url);
        }
    });
}

實現(xiàn):

 myObservableUrls.flatMap(new Func1<List<String>, Observable<String>>() {
        @Override
        public Observable<String> call(List<String> urls) {
            return Observable.from(urls);
        }
    }).flatMap(new Func1<String, Observable<?>>() {
        @Override
        public Observable<?> call(String s) {
            return getTitle(s);
        }
    }).subscribe(new Action1<Object>() {
        @Override
        public void call(Object o) {
            L.d("myObservableUrls.flatMap onNext o: " + o);
        }
    });

filter()

filter()輸出和輸入相同的元素伪煤,并且會過濾掉那些不滿足檢查條件的

 myObservableUrls.flatMap(new Func1<List<String>, Observable<String>>() {
        @Override
        public Observable<String> call(List<String> urls) {
            return Observable.from(urls);
        }
    }).flatMap(new Func1<String, Observable<?>>() {
        @Override
        public Observable<?> call(String s) {
            return getTitle(s);
        }
    }).filter(new Func1<Object, Boolean>() {
        @Override
        public Boolean call(Object o) {
            return !o.equals("title: url2");
        }
    }).subscribe(new Action1<Object>() {
        @Override
        public void call(Object o) {
            L.d("myObservableUrls.flatMap onNext o: " + o);
        }
    });

take()

take()輸出最多指定數(shù)量的結(jié)果扁誓。

doOnNext()

doOnNext()允許我們在每次輸出一個元素之前做一些額外的事情蝗敢,比如這里的保存標(biāo)題锁右。

直接上網(wǎng)上的示例:

query("Hello, world!")  
.flatMap(urls -> Observable.from(urls))  
.flatMap(url -> getTitle(url))  
.filter(title -> title != null)  
.take(5)  
.doOnNext(title -> saveTitle(title))  
.subscribe(title -> System.out.println(title));  

三码泞、響應(yīng)式的好處

錯誤處理

每一個Observerable對象在終結(jié)的時候都會調(diào)用onCompleted()或者onError()方法领铐,

這種模式有以下幾個優(yōu)點:

1.只要有異常發(fā)生onError()一定會被調(diào)用

這極大的簡化了錯誤處理。只需要在一個地方處理錯誤即可以莲兢。

2.操作符不需要處理異常

將異常處理交給訂閱者來做坟岔,Observerable的操作符調(diào)用鏈中一旦有一個拋出了異常,就會直接執(zhí)行onError()方法燕鸽。

3.你能夠知道什么時候訂閱者已經(jīng)接收了全部的數(shù)據(jù)鸥拧。

知道什么時候任務(wù)結(jié)束能夠幫助簡化代碼的流程。(雖然有可能Observable對象永遠(yuǎn)不會結(jié)束)

使用RXJAVA腕柜,OBSERVABLE對象根本不需要知道如何處理錯誤!操作符也不需要處理錯誤狀態(tài)-一旦發(fā)生錯誤娩脾,就會跳過當(dāng)前和后續(xù)的操作符俩功。所有的錯誤處理都交給訂閱者來做。

調(diào)度器

使用RxJava碰声,你可以使用subscribeOn()指定觀察者代碼運行的線程诡蜓,使用observerOn()指定訂閱者運行的線程:

myObservableServices.retrieveImage(url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

訂閱(Subscriptions)

調(diào)用Observable.subscribe(),會返回一個Subscription對象胰挑。這個對象代表了被觀察者和訂閱者之間的聯(lián)系蔓罚。

ubscription subscription = Observable.just("Hello, World!")
.subscribe(s -> System.out.println(s));

你可以在后面使用這個Subscription對象來操作被觀察者和訂閱者之間的聯(lián)系.

subscription.unsubscribe();
System.out.println("Unsubscribed=" + subscription.isUnsubscribed());

RxJava的另外一個好處就是它處理unsubscribing的時候贡这,會停止整個調(diào)用鏈。如果你使用了一串很復(fù)雜的操作符喜爷,調(diào)用unsubscribe將會在他當(dāng)前執(zhí)行的地方終止泛源。不需要做任何額外的工作!

當(dāng)然也不是所有的代碼都使用響應(yīng)式的方式溪王,僅僅當(dāng)代碼復(fù)雜到我想將它分解成簡單的邏輯的時候,我才使用響應(yīng)式代碼掐禁。

四教届、在Android中使用響應(yīng)式編程

這部分主要是摘錄倦卖,有些方法現(xiàn)在已過時昧狮,后續(xù)在實際應(yīng)用中有用到再進(jìn)行補充更新,

RxAndroid

首先,AndroidSchedulers提供了針對Android的線程系統(tǒng)的調(diào)度器糠悯。需要在UI線程中運行某些代碼?很簡單烤芦,只需要使用AndroidSchedulers.mainThread():

retrofitService.getImage(url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

throttleFirst(): 在每次事件觸發(fā)后的一定時間間隔內(nèi)丟棄新的事件陪蜻。常用作去抖動過濾,例如按鈕的點擊監(jiān)聽器:

RxView.clickEvents(button) // RxBinding 代碼花颗,后面的文章有解釋
.throttleFirst(500, TimeUnit.MILLISECONDS) // 設(shè)置防抖間隔為 500ms
.subscribe(subscriber);

遺留代碼,運行極慢的代碼

絕大多數(shù)時候Observable.just() 和 Observable.from() 能夠幫助你從遺留代碼中創(chuàng)建 Observable 對象:

private Object oldMethod() { ... }

public Observable<Object> newMethod() {
    return Observable.just(oldMethod());
}

上面的例子中如果oldMethod()足夠快是沒有什么問題的剩辟,但是如果很慢呢卦羡?調(diào)用oldMethod()將會阻塞住他所在的線程。
為了解決這個問題呻纹,可以參考我一直使用的方法–使用defer()來包裝緩慢的代碼:

private Object slowBlockingMethod() { ... }

public Observable<Object> newMethod() {
    return Observable.defer(() -> Observable.just(slowBlockingMethod()));
}

現(xiàn)在,newMethod()的調(diào)用不會阻塞了专缠,除非你訂閱返回的observable對象雷酪。

生命周期

如何處理Activity的生命周期?主要就是兩個問題:
1.在configuration改變(比如轉(zhuǎn)屏)之后繼續(xù)之前的Subscription涝婉。
比如你使用Retrofit發(fā)出了一個REST請求哥力,接著想在listview中展示結(jié)果。如果在網(wǎng)絡(luò)請求的時候用戶旋轉(zhuǎn)了屏幕怎么辦墩弯?你當(dāng)然想繼續(xù)剛才的請求吩跋,但是怎么搞?

2.Observable持有Context導(dǎo)致的內(nèi)存泄露
這個問題是因為創(chuàng)建subscription的時候渔工,以某種方式持有了context的引用锌钮,尤其是當(dāng)你和view交互的時候,這太容易發(fā)生引矩!如果Observable沒有及時結(jié)束梁丘,內(nèi)存占用就會越來越大侵浸。

第一個問題的解決方案就是使用RxJava內(nèi)置的緩存機制,這樣你就可以對同一個Observable對象執(zhí)行unsubscribe/resubscribe氛谜,卻不用重復(fù)運行得到Observable的代碼掏觉。cache() (或者 replay())會繼續(xù)執(zhí)行網(wǎng)絡(luò)請求(甚至你調(diào)用了unsubscribe也不會停止)。這就是說你可以在Activity重新創(chuàng)建的時候從cache()的返回值中創(chuàng)建一個新的Observable對象值漫。

Observable<Photo> request = service.getUserPhoto(id).cache();
Subscription sub = request.subscribe(photo -> handleUserPhoto(photo));

// ...When the Activity is being recreated...
sub.unsubscribe();

// ...Once the Activity is recreated...
request.subscribe(photo -> handleUserPhoto(photo));

注意澳腹,兩次sub是使用的同一個緩存的請求。當(dāng)然在哪里去存儲請求的結(jié)果還是要你自己來做杨何,和所有其他的生命周期相關(guān)的解決方案一延虎酱塔,必須在生命周期外的某個地方存儲。(retained fragment或者單例等等)危虱。

第二個問題的解決方案就是在生命周期的某個時刻取消訂閱延旧。一個很常見的模式就是使用CompositeSubscription來持有所有的Subscriptions,然后在onDestroy()或者onDestroyView()里取消所有的訂閱槽地。

private CompositeSubscription mCompositeSubscription
= new CompositeSubscription();

private void doSomething() {
    mCompositeSubscription.add(
        AndroidObservable.bindActivity(this, Observable.just("Hello, World!"))
        .subscribe(s -> System.out.println(s)));
}

@Override
protected void onDestroy() {
    super.onDestroy();

    mCompositeSubscription.unsubscribe();
}

你可以在Activity/Fragment的基類里創(chuàng)建一個CompositeSubscription對象迁沫,在子類中使用它。

注意! 一旦你調(diào)用了 CompositeSubscription.unsubscribe()捌蚊,這個CompositeSubscription對象就不可用了, 如果你還想使用CompositeSubscription集畅,就必須在創(chuàng)建一個新的對象了。
最后缅糟,附上測試時使用的完整工程挺智,代碼上面均有貼出來了。
RxAndroid基本使用測試代碼
http://download.csdn.net/detail/yaodong379/9486905

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窗宦,一起剝皮案震驚了整個濱河市赦颇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赴涵,老刑警劉巖媒怯,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異髓窜,居然都是意外死亡扇苞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門寄纵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鳖敷,“玉大人,你說我怎么就攤上這事程拭《猓” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵恃鞋,是天一觀的道長崖媚。 經(jīng)常有香客問我亦歉,道長,這世上最難降的妖魔是什么至扰? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任鳍徽,我火速辦了婚禮资锰,結(jié)果婚禮上敢课,老公的妹妹穿的比我還像新娘。我一直安慰自己绷杜,他們只是感情好直秆,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鞭盟,像睡著了一般圾结。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上齿诉,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天筝野,我揣著相機與錄音,去河邊找鬼粤剧。 笑死歇竟,一個胖子當(dāng)著我的面吹牛决左,可吹牛的內(nèi)容都是我干的秃流。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼疮绷,長吁一口氣:“原來是場噩夢啊……” “哼弧关!你這毒婦竟也來了盅安?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤世囊,失蹤者是張志新(化名)和其女友劉穎别瞭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體株憾,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡畜隶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了号胚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片籽慢。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖猫胁,靈堂內(nèi)的尸體忽然破棺而出箱亿,到底是詐尸還是另有隱情,我是刑警寧澤弃秆,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布届惋,位于F島的核電站髓帽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏脑豹。R本人自食惡果不足惜郑藏,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘩欺。 院中可真熱鬧必盖,春花似錦、人聲如沸俱饿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拍埠。三九已至失驶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枣购,已是汗流浹背嬉探。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棉圈,地道東北人涩堤。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像迄损,于是被迫代替她去往敵國和親定躏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361

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