瞭解RxJava之Android響應式編程(四)

原文鏈接:Grokking RxJava, Part 4: Reactive Android

在前三部分,我介紹了RxJava如何工作。但作爲一個Android開發者,如何讓它爲你工作?這裏有些對於Android開發者實用的信息。

0x00 RxAndroid

RxAndroid 是專爲Android設計的RxJava擴展,它包含各類工具簡化Android應用開發。

首先,AndroidSchedulers調度器是針對Android線程系統而設計。想要在UI線程運行?沒問題,只需使用AndroidSchedulers.mainThread()。

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

如果你有自己的Handler,您可通過HandlerThreadScheduler1把它鏈接到你的Handler。

接下來,我們介紹AndroidObservable,它爲Android生命週期內工作提供了便利。它提供bindActivity()和bindFragment(),自動使用AndroidSchedulers.mainThread(),Activity 或者 Fragment結束的時候通知。(注: Observable與Android生命週期關聯,已經已入RxLifecycle庫)

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

我也喜歡AndroidObservable.fromBroadcast()方法,它允許你創建一個Observable類型BroadcastReceiver。下面展示網絡變化時發出通知。

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

最後,我們介紹ViewObservable,它爲View提供了一些綁定。ViewObservable.clicks():這個方法讓你在View被點擊時做出響應,ViewObservable.text():觀察TextView內容改變。(現在可以RxBinding庫中找到這些內容)

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

0x02 Retrofit

著名的Retrofit庫已經支持RxJava,Retrofit是“Type-safe HTTP client for Android and Java”。通常定義一個異步方法需要添加一個回調方法(Callback):

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

現在通過Observable能做到你想做的,不但能獲取到數據,同時能對數據進行修改。

Retrofit支持Observable與HTTP調用相結合。比如:我們有一個方法需要先取得用戶圖片,然後取得用戶基本信息數據。我們可以通過RxJava的zip()操作符把兩個結果合併在一起。

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

第二篇中我展示過使用flatMap()操作符的例子。下面我想要展示的是使用RxJava + Retrofit組合多個HTTP請求是多麼的簡單。

0x03 處理遺留,緩慢的代碼

Retrofit返回Observables使邏輯變得多個簡潔。但是如果你使用的其他庫不支持RxJava呢?或者你程序內部的代碼想過轉換爲Ob?基本上,如何在新系統中轉換遺留代碼而不需重寫一切?

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在調用subscribe()方法錢都不會調用slowBlockingMethod()。

0x04 生命週期

最後,我來解決最困難的部分。你如何處理Activity生命週期?有兩個難纏的問題:
1. 在配置改變之間(如屏幕旋轉)保持Subscription
假設你使用Retrofit構建了一個HTTP請求,然後在ListView展示結果。如果網絡請求還沒結束的時候,用戶旋轉了屏幕怎麼辦?你想保持剛纔的請求,怎麼做呢?
2. Observables 持有Context導致內存泄漏
這個問題是由subscription持有Context什麼引起的,這樣使得與Views交互變得容易。Observable不停止,你可能最終保留了很多額外的內存。

不幸的是,對於任何一個問題沒有靈丹妙藥,但也有一些可以遵循,使您的生活更輕鬆的指引。

第一個問題可以用一些RxJava內置的緩存機制來解決,這樣你可以對同一個Observable執行退訂/訂閱,而不需要重複獲取Observable的過程。特別是,cache()(或者replay())會繼續執行請求(甚至你unsubscribe)。這意味着你可以在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));

請注意,我們在兩種情況使用相同的緩存請求,這種方式底層調用只發生一次,不管你在哪裏緩存請求,但是類似所有的生命週期解決方案,都必須在生命週期中某處存儲請求(類似fragment或者單例等)。

第二個問題解決方案是subscriptions調用unsubscribe方法。這裏存在一個普遍的模式:使用CompositeSubscription來保持所有的Subscriptions,然後在onDestroy() 或者 onDestroyView()時調用unsubscribe()方法。

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,並且在內部創建CompositeSubscription ,生命週期最後自動unsubscribed。

警告!CompositeSubscription.unsubscribe() 不能夠手動調用,換句話說就是有庫自動調用。如果您計劃重新使用這種模式之後,你必須創建一個新的CompositeSubscription作爲替代。

解決這兩個問題涉及添加代碼,解決這兩個問題沒有所有的樣本。

0x04 結論

RxJava仍然是相當新的,針對Android的適配甚至更新。RxAndroid是處於活躍的開發狀態,當前我在這裏給出的建議以後每年可能都被視爲古怪。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章