深入淺出RxJava(四:在Android中使用響應式編程)

文章轉(zhuǎn)自:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0426/2787.html

原文鏈接 http://blog.danlew.net/2014/10/08/grokking-rxjava-part-4/
譯文鏈接 http://blog.csdn.net/lzyzsd/article/details/45033611

在第123篇中,我大概介紹了RxJava是怎么使用的。下面我會介紹如何在Android中使用RxJava.

RxAndroid

RxAndroid是RxJava的一個針對Android平臺的擴展。它包含了一些能夠簡化Android開發(fā)的工具。

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

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

如果你已經(jīng)創(chuàng)建了自己的Handler,你可以使用HandlerThreadScheduler1將一個調(diào)度器鏈接到你的handler上。
接著要介紹的就是AndroidObservable,它提供了跟多的功能來配合Android的生命周期。bindActivity()和 bindFragment()方法默認使用AndroidSchedulers.mainThread()來執(zhí)行觀察者代碼,這兩個方法會在 Activity或者Fragment結(jié)束的時候通知被觀察者停止發(fā)出新的消息。

AndroidObservable.bindActivity(this, retrofitService.getImage(url))
    .subscribeOn(Schedulers.io())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap);

我自己也很喜歡AndroidObservable.fromBroadcast()方法,它允許你創(chuàng)建一個類似BroadcastReceiver的Observable對象。下面的例子展示了如何在網(wǎng)絡(luò)變化的時候被通知到:

IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
AndroidObservable.fromBroadcast(context, filter)
    .subscribe(intent -> handleConnectivityChange(intent));

最后要介紹的是ViewObservable,使用它可以給View添加了一些綁定。如果你想在每次點擊view的時候都收到一個事件,可以使用 ViewObservable.clicks(),或者你想監(jiān)聽TextView的內(nèi)容變化,可以使用ViewObservable.text()。

ViewObservable.clicks(mCardNameEditText, false)
    .subscribe(view -> handleClick(view));

Retrofit

大名鼎鼎的Retrofit庫內(nèi)置了對RxJava的支持。通常調(diào)用發(fā)可以通過使用一個Callback對象來獲取異步的結(jié)果:

@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);

使用RxJava,你可以直接返回一個Observable對象。

@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);

現(xiàn)在你可以隨意使用Observable對象了。你不僅可以獲取數(shù)據(jù),還可以進行變換。 Retrofit對Observable的支持使得它可以很簡單的將多個REST請求結(jié)合起來。比如我們有一個請求是獲取照片的,還有一個請求是獲取元數(shù)據(jù)的,我們就可以將這兩個請求并發(fā)的發(fā)出,并且等待兩個結(jié)果都返回之后再做處理:

Observable.zip(
    service.getUserPhoto(id),
    service.getPhotoMetadata(id),
    (photo, metadata) -> createPhotoWithData(photo, metadata))
    .subscribe(photoWithData -> showPhoto(photoWithData));

在第二篇里我展示過一個類似的例子(使用flatMap())。這里我只是想展示以下使用RxJava+Retrofit可以多么簡單地組合多個REST請求。
遺留代碼,運行極慢的代碼
Retrofit可以返回Observable對象,但是如果你使用的別的庫并不支持這樣怎么辦?或者說一個內(nèi)部的內(nèi)碼,你想把他們轉(zhuǎn)換成Observable的?有什么簡單的辦法沒?
絕大多數(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)了屏幕怎么辦?你當然想繼續(xù)剛才的請求,但是怎么搞?
2.Observable持有Context導致的內(nèi)存泄露
這個問題是因為創(chuàng)建subscription的時候,以某種方式持有了context的引用,尤其是當你和view交互的時候,這太容易發(fā)生!如果Observable沒有及時結(jié)束,內(nèi)存占用就會越來越大。 不幸的是,沒有銀彈來解決這兩個問題,但是這里有一些指導方案你可以參考。
第一個問題的解決方案就是使用RxJava內(nèi)置的緩存機制,這樣你就可以對同一個Observable對象執(zhí)行 unsubscribe/resubscribe,卻不用重復運行得到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是使用的同一個緩存的請求。當然在哪里去存儲請求的結(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)建一個新的對象了。
兩個問題的解決方案都需要添加額外的代碼,如果誰有更好的方案,歡迎告訴我。

總結(jié)

RxJava還是一個很新的項目,RxAndroid更是。RxAndroid目前還在活躍開發(fā)中,也沒有多少好的例子。我打賭一年之后我的一些建議就會被看做過時了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 文章轉(zhuǎn)載自:Hi大頭鬼_深入淺出RxJava四-在Android中使用響應式編程個人感覺CSDN上看起來字體不是很...
    福祿娃閱讀 639評論 1 3
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,268評論 25 708
  • 我從去年開始使用 RxJava ,到現(xiàn)在一年多了。今年加入了 Flipboard 后,看到 Flipboard 的...
    Jason_andy閱讀 5,570評論 7 62
  • 20171001 一、本周計劃 核算本周時間起止日期:10月1日-10月7日,總共7天總共:24x7=168小時睡...
    RicciWoo閱讀 418評論 0 51
  • 早上上學前,兒子灌好自己的水壺,然后對我說:媽媽,壺里沒水了,我燒了一壺水給你,你送完我回來正好喝,別忘了加紅糖!
    Maggie0618閱讀 168評論 0 0