在前三部分才顿,我在通用層面介紹了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,在Activity
或Fragment
結束的時候僵腺。另外會自動為訂閱使用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)建了一個以某種方式持有
Context
的subscribtion
,特別是你和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)建一個根Activity
或Fragment
,順帶添加一個CompositeSubscription
坞琴,隨后可以相應的取消訂閱哨查。
注意,只要你調用了CompositeSubscription.unsubscribe()
剧辐,該對象(`CompositeSubscription`)就不能用了寒亥。因為它會自動取消訂閱隨后你添加的任何事物。如果你今后想要使用這種方法荧关,你必須創(chuàng)建一個新的CompositeSubscription
作為替代溉奕。
兩個問題的解決方法都涉及到添加代碼,我希望有一天能出現(xiàn)不需要寫這些樣板代碼就能解決這些問題的天才忍啤。
本文翻譯自Grokking RxJava, Part 4: Reactive Android,著作權歸原作者danlew所有加勤。譯文由JohnTsai翻譯。轉載請注明出處同波,并保留此段聲明鳄梅。