深入淺出RxJava四-在Android中使用響應式編程 <譯>

文章轉(zhuǎn)載自:Hi大頭鬼_深入淺出RxJava四-在Android中使用響應式編程
個人感覺CSDN上看起來字體不是很舒服隔躲,所以轉(zhuǎn)載一下际长。

英文原文
在第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畏吓,你可以使用HandlerThreadScheduler將一個調(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)建一個類似BroadcastReceiverObservable對象镐确。下面的例子展示了如何在網(wǎng)絡變化的時候被通知到:

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)絡請求的時候用戶旋轉(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)絡請求(甚至你調(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é)果還是要你自己來做,和所有其他的生命周期相關的解決方案一延虎疮鲫,必須在生命周期外的某個地方存儲吆你。(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ā)中讥耗,也沒有多少好的例子有勾。我打賭一年之后我的一些建議就會被看做過時了。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末古程,一起剝皮案震驚了整個濱河市柠衅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌籍琳,老刑警劉巖菲宴,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異趋急,居然都是意外死亡喝峦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門呜达,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谣蠢,“玉大人,你說我怎么就攤上這事查近∶减猓” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵霜威,是天一觀的道長谈喳。 經(jīng)常有香客問我,道長戈泼,這世上最難降的妖魔是什么婿禽? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮大猛,結(jié)果婚禮上扭倾,老公的妹妹穿的比我還像新娘。我一直安慰自己挽绩,他們只是感情好膛壹,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唉堪,像睡著了一般模聋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巨坊,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天撬槽,我揣著相機與錄音,去河邊找鬼趾撵。 笑死侄柔,一個胖子當著我的面吹牛共啃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播暂题,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼移剪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了薪者?” 一聲冷哼從身側(cè)響起纵苛,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎言津,沒想到半個月后攻人,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡悬槽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年怀吻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片初婆。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蓬坡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出磅叛,到底是詐尸還是另有隱情屑咳,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布弊琴,位于F島的核電站兆龙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏访雪。R本人自食惡果不足惜详瑞,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望臣缀。 院中可真熱鬧,春花似錦泻帮、人聲如沸精置。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脂倦。三九已至,卻和暖如春元莫,著一層夾襖步出監(jiān)牢的瞬間赖阻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工踱蠢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留火欧,地道東北人棋电。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像苇侵,于是被迫代替她去往敵國和親赶盔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348

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