理解RxJava:(四)Reactive Android

在前三部分,我在通用層面介紹了RxJava的工做原理。可是做爲一個Android開發者,如何在工做中使用它呢?下面是一些給Android開發者的RxJava的具體應用。html

RxAndroid

RxAndroid是RxJava在Android開發中的拓展。它包含能節省咱們大量時間的特殊bindings。java

首先,其中有AndroidSchedulers,它能提供專門爲Android線程系統提供的schedulers。須要在UI線程運行代碼?沒問題——只須要使用AndroidSchedulers.mainThread()方法便可:android

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

若是你拿到的是Handler,能夠經過HandlerThreadScheduler建立一個scheduler綁定在Handler上。git

接下來介紹的是AndroidObservable,它能提供不少在Android生命週期中的特點功能。bindActivity()bindFragment()方法能中止發出items,在ActivityFragment結束的時候。另外會自動爲訂閱使用AndroidSchedulers.mainThread()。(所以你不須要在Activity或Fragment無效的時候來改變狀態)。github

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

我也喜歡AndroidObservable.fromBroadcast(),它讓咱們能夠建立一個像BroadcastReceiver那樣工做的Observable。如下是不管何時網絡鏈接發生變化時通知的方法。緩存

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

最後介紹的是ViewObservable,它能爲Views添加bindings。若是你想要獲得View每次被點擊的事件,能夠經過ViewObservable.clicks()方法。也能夠經過ViewObservable.text()方法來監測TextView的內容發生的任何變化。網絡

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

Retrofit

有一個知名而且支持RxJava的庫:Retrofit,它是Android開發中的很是出名的REST風格的網絡庫。一般,咱們定義一個異步方法並添加Callback:異步

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

用了RxJava,能夠用Observable代替Callback做爲返回值。ide

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

如今能夠對Observable作你想要的操做了,不只能夠得到數據,也能變換它。this

Retrofit支持Observable,也使得合併多個REST請求變得容易。例如,假設咱們有一個請求得到圖片,另外一個請求得到元數據(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?總之,你如何將舊的代碼和新的代碼聯繫在一塊兒,而不用重寫全部代碼?

Observable.just()Observable.from()大多數時候足以將之前的代碼轉換爲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()));
}

如今, 直到你訂閱Observable纔會去調用slowBlockMethod()方法。

生命週期

我把最難的部分留在了最後。你是如何處理RxJava與Activity的生命週期的(配合使用的)?如下兩個問題會屢次出現:

  • 1.Activity的配置發生變化後繼續訂閱一個Subscribtion

    假設你用Retrofit作了一次REST請求,想要把請求結果展現在ListView上。若是用戶旋轉了屏幕怎麼辦?若是你想要繼續相同的請求,可是如何作呢?

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

    這個問題是因爲建立了一個以某種方式持有Contextsubscribtion,特別是你和Views交互的時候容易出現。若是Observable沒有準時完成,最後可能持有很是多的額外內存。

不幸的是,兩個問題都沒有很好的解決辦法。可是有一些能節約你時間的指導方針。

第一個問題能用一些RxJava內置的緩存機制解決,所以你能夠取消訂閱/再訂閱同一個Observable,而不須要重複它以前的(準備)工做。特別的,cache()(或是replay())方法將繼續在(方法)之下的請求(即便你取消訂閱)。這意味着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),那種方式隱含的調用只會發生一次。存放請求的地方你本身決定,可是像全部的生命週期解決方案同樣,它必須存放在生命週期以外的地方(一個保存的fragment,單例等等)。

第二問題能夠經過根據生命週期正確的取消訂閱來實現。通用的方法是用一個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();
}

更進一步,你能夠建立一個根ActivityFragment,順帶添加一個CompositeSubscription,隨後能夠相應的取消訂閱。

注意,只要你調用了CompositeSubscription.unsubscribe(),該對象(`CompositeSubscription`)就不能用了。由於它會自動取消訂閱隨後你添加的任何事物。若是你從此想要使用這種方法,你必須建立一個新的CompositeSubscription做爲替代。

兩個問題的解決方法都涉及到添加代碼,我但願有一天能出現不須要寫這些樣板代碼就能解決這些問題的天才。

本文翻譯自Grokking RxJava, Part 4: Reactive Android,著做權歸原做者danlew全部。譯文由JohnTsai翻譯。轉載請註明出處,並保留此段聲明。

相關文章
相關標籤/搜索