理解RxJava:(四)Reactive Android

在前三部分才顿,我在通用層面介紹了RxJava的工作原理碉纺。但是作為一個Android開發(fā)者号胚,如何在工作中使用它呢?下面是一些給Android開發(fā)者的RxJava的具體應用愉适。

RxAndroid

RxAndroid是RxJava在Android開發(fā)中的拓展。它包含能節(jié)省我們大量時間的特殊bindings栗竖。

首先暑脆,其中有AndroidSchedulers,它能提供專門為Android線程系統(tǒng)提供的schedulers狐肢。需要在UI線程運行代碼?沒問題——只需要使用AndroidSchedulers.mainThread()方法即可:

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

如果你拿到的是Handler添吗,可以通過HandlerThreadScheduler創(chuàng)建一個scheduler綁定在Handler上。

接下來介紹的是AndroidObservable份名,它能提供很多在Android生命周期中的特色功能碟联。bindActivity()bindFragment()方法能停止發(fā)出items,在ActivityFragment結束的時候僵腺。另外會自動為訂閱使用AndroidSchedulers.mainThread()鲤孵。(因此你不需要在Activity或Fragment無效的時候來改變狀態(tài))。

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

我也喜歡AndroidObservable.fromBroadcast()辰如,它讓我們可以創(chuàng)建一個像BroadcastReceiver那樣工作的Observable普监。以下是無論什么時候網(wǎng)絡連接發(fā)生變化時通知的方法。

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

最后介紹的是ViewObservable琉兜,它能為Views添加bindings凯正。如果你想要得到View每次被點擊的事件,可以通過ViewObservable.clicks()方法豌蟋。也可以通過ViewObservable.text()方法來監(jiān)測TextView的內容發(fā)生的任何變化廊散。

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

Retrofit

有一個知名并且支持RxJava的庫:Retrofit,它是Android開發(fā)中的非常出名的REST風格的網(wǎng)絡庫夺饲。通常奸汇,我們定義一個異步方法并添加Callback:

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

用了RxJava,可以用Observable代替Callback作為返回值往声。

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

現(xiàn)在可以對Observable做你想要的操作了擂找,不僅可以獲得數(shù)據(jù),也能變換它浩销。

Retrofit支持Observable贯涎,也使得合并多個REST請求變得容易。例如慢洋,假設我們有一個請求獲得圖片塘雳,另一個請求獲得元數(shù)據(jù)(metadata)。我們可以把結果組合到一起:

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

我在第二部分展示了相似的例子(使用flatMap())普筹。這便證明使用RxJava+Retrofit合并多個REST請求有多簡單败明。

舊的,耗時長的代碼

Retrofit能返回Observerables這固然非常好太防,但是如果你用的其他的庫不支持它呢妻顶?或者你想要將一些內部代碼轉換為Observables?總之,你如何將舊的代碼和新的代碼聯(lián)系在一起讳嘱,而不用重寫所有代碼?

Observable.just()Observable.from()大多數(shù)時候足以將以前的代碼轉換為Observable

private Object oldMethod() { ... }

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

這在oldMethod()耗時少的情況下能正常工作幔嗦,但是如果耗時長呢?因為調用oldMethod()沥潭,在它被傳遞到Observable.just()方法前就會阻塞了線程邀泉。

為了對付這個問題,以下是我一直使用的方法——使用defer方法將耗時長的部分包裝起來钝鸽。

private Object slowBlockingMethod() { ... }

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

現(xiàn)在汇恤, 直到你訂閱Observable才會去調用slowBlockMethod()方法。

生命周期

我把最難的部分留在了最后寞埠。你是如何處理RxJava與Activity的生命周期的(配合使用的)屁置?以下兩個問題會多次出現(xiàn):

  • 1.Activity的配置發(fā)生變化后繼續(xù)訂閱一個Subscribtion

    假設你用Retrofit做了一次REST請求,想要把請求結果展示在ListView上仁连。如果用戶旋轉了屏幕怎么辦蓝角?如果你想要繼續(xù)相同的請求,但是如何做呢饭冬?

  • 2.Observables持有Context引用會造成內存泄漏使鹅。

    這個問題是由于創(chuàng)建了一個以某種方式持有Contextsubscribtion,特別是你和Views交互的時候容易出現(xiàn)昌抠。如果Observable沒有準時完成患朱,最后可能持有非常多的額外內存。

不幸的是炊苫,兩個問題都沒有很好的解決辦法裁厅。但是有一些能節(jié)約你時間的指導方針。

第一個問題能用一些RxJava內置的緩存機制解決侨艾,因此你可以取消訂閱/再訂閱同一個Observable执虹,而不需要重復它之前的(準備)工作。特別的唠梨,cache()(或是replay())方法將繼續(xù)在(方法)之下的請求(即使你取消訂閱)袋励。這意味著Activity重新生成時,你能重新開始新的subscription。

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));

注意我們在兩種情況下当叭,用的是同一個緩存的請求(request)茬故,那種方式隱含的調用只會發(fā)生一次。存放請求的地方你自己決定蚁鳖,但是像所有的生命周期解決方案一樣磺芭,它必須存放在生命周期之外的地方(一個保存的fragment,單例等等)醉箕。

第二問題可以通過根據(jù)生命周期正確的取消訂閱來實現(xiàn)钾腺。通用的方法是用一個CompositeSubscription來持有所有的Subscription甘邀,然后在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();
}

更進一步垮庐,你可以創(chuàng)建一個根ActivityFragment,順帶添加一個CompositeSubscription坞琴,隨后可以相應的取消訂閱哨查。

注意,只要你調用了CompositeSubscription.unsubscribe()剧辐,該對象(`CompositeSubscription`)就不能用了寒亥。因為它會自動取消訂閱隨后你添加的任何事物。如果你今后想要使用這種方法荧关,你必須創(chuàng)建一個新的CompositeSubscription作為替代溉奕。

兩個問題的解決方法都涉及到添加代碼,我希望有一天能出現(xiàn)不需要寫這些樣板代碼就能解決這些問題的天才忍啤。

本文翻譯自Grokking RxJava, Part 4: Reactive Android,著作權歸原作者danlew所有加勤。譯文由JohnTsai翻譯。轉載請注明出處同波,并保留此段聲明鳄梅。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市未檩,隨后出現(xiàn)的幾起案子戴尸,更是在濱河造成了極大的恐慌,老刑警劉巖冤狡,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孙蒙,死亡現(xiàn)場離奇詭異,居然都是意外死亡悲雳,警方通過查閱死者的電腦和手機挎峦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怜奖,“玉大人浑测,你說我怎么就攤上這事⊥崃幔” “怎么了迁央?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滥崩。 經(jīng)常有香客問我岖圈,道長,這世上最難降的妖魔是什么钙皮? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任蜂科,我火速辦了婚禮顽决,結果婚禮上,老公的妹妹穿的比我還像新娘导匣。我一直安慰自己才菠,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布贡定。 她就那樣靜靜地躺著赋访,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缓待。 梳的紋絲不亂的頭發(fā)上蚓耽,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音旋炒,去河邊找鬼步悠。 笑死,一個胖子當著我的面吹牛瘫镇,可吹牛的內容都是我干的鼎兽。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼汇四,長吁一口氣:“原來是場噩夢啊……” “哼接奈!你這毒婦竟也來了?” 一聲冷哼從身側響起通孽,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤序宦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后背苦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體互捌,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年行剂,在試婚紗的時候發(fā)現(xiàn)自己被綠了秕噪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡厚宰,死狀恐怖腌巾,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情铲觉,我是刑警寧澤澈蝙,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站撵幽,受9級特大地震影響灯荧,放射性物質發(fā)生泄漏。R本人自食惡果不足惜盐杂,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一逗载、第九天 我趴在偏房一處隱蔽的房頂上張望哆窿。 院中可真熱鬧,春花似錦厉斟、人聲如沸挚躯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秧均。三九已至,卻和暖如春号涯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锯七。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工链快, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人眉尸。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓域蜗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親噪猾。 傳聞我的和親對象是個殘疾皇子霉祸,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容