Kotlin 使用Rxjava的compose()操作符

weixin_34248705發表於2018-03-05

轉載

在RXJava中一種比較nice的思想是能夠通過一系列的操作符看到資料是如何轉換的:

Observable.from(someSource)  
    .map(data -> manipulate(data))
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(data -> doSomething(data));

假設想要給多個流重複利用一系列操作符該怎麼辦呢?比如,我想在工作執行緒中處理資料,在主執行緒中訂閱,所以必須頻繁使用subscribeOn() 和observeOn()。如果我能將這邏輯通過連續的、可重複的方式應用到我的所有流上,那簡直太棒了。

壞方法

下面這個方法是我用過數月的“反模式”方法。首先,建立一個方法,該方法適用於排程器

<T> Observable<T> applySchedulers(Observable<T> observable) {  
    return observable.subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());
}

然後呼叫applySchedulers(),包裝你的Observable鏈:

applySchedulers(  
    Observable.from(someSource)
        .map(data -> manipulate(data))
    )
    .subscribe(data -> doSomething(data));

雖然這樣寫能執行但是很醜很亂——因為applySchedulers()導致了它不再是操作符,因此很難在其後面追加其他操作符。現在,當你多用幾次這種“反模式”在單一流上之後你就會覺得它有多壞了。

Transformers登場

在RXJava背後有一群聰明的人意識到了這是一個問題並且提供瞭解決方案:Transformer ,它和 Observable.compose() 一起使用。

Transformer實際上就是Func1<Observable , Observable

,換句話說就是提供給他一個Observable它會返回給你另一個Observable,這和內聯一系列操作符有著同等功效。

實際操作下,寫個方法,建立一個Transformer排程器:

<T> Transformer<T, T> applySchedulers() {  
  return new Transformer<T, T>() {
    @Override
    public Observable<T> call(Observable<T> observable) {
      return observable.subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());
    }
  };
}

用lambda表示式看上去會好看些:

<T> Transformer<T, T> applySchedulers() {  
    return observable -> observable.subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());
}

現在呼叫這個方法會是怎麼樣的呢:

Observable.from(someSource)  
    .map(data -> manipulate(data))
    .compose(applySchedulers())
    .subscribe(data -> doSomething(data));

是不是好很多!我們既重用了程式碼又保護了鏈式。如果你用的是JDK 7以下版本,你不得不利用compose() 做一些多餘的工作。比如說,你得告訴編譯器返回的型別,像這樣:

Observable.from(someSource)  
    .map(data -> manipulate(data))
    .compose(this.<YourType>applySchedulers())
    .subscribe(data -> doSomething(data));

複用Transformers

如果你經常做從一個具體型別轉換到另一個型別來建立一個例項:

Transformer<String, String> myTransformer = new Transformer<String, String>() {  
    // 幹一些事情
};

用Transformer會怎麼樣呢?它壓根就不會考慮型別問題,但你不能定義一個通用的例項。

你可以把它改成Transformer<Object, Object>,但是返回的Observable會丟失它的型別資訊。

解決這個問題我是從 Collections 中得到了靈感,Collections是一堆型別安全、不可變的空集合的生成方法(比如 Collections.emptyList() )。本質上它採用了非泛型例項,然後通過方法包裹附加泛型資訊。

final Transformer schedulersTransformer =  
  observable -> observable.subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread());
@SuppressWarnings("unchecked")
<T> Transformer<T, T> applySchedulers() {  
  return (Transformer<T, T>) schedulersTransformer;
}

警告:無論如何你都有可能陷入型別轉換異常的坑。確保你的Transformer真的是型別無關的。另一方面,你可能會在執行時丟擲ClassCastException異常,即使你的程式碼編譯通過了。在這個例子裡面,因為排程器沒有和發射的專案發生互動,所以它是型別安全的。

和flatMap()有啥區別?

compose()和flatMap()有啥區別呢。他們都是發射出Observable

,是不是就是說他們都可以複用一系列操作符呢?

區別在於compose()是高等級的抽象,他操作的是整個流,而不是單一發射出的專案,這裡有更多的解釋:

  1. compose()
    是唯一一個能從流中獲取原生Observable 的方法,因此,影響整個流的操作符(像subscribeOn()和observeOn())需要使用compose(),相對的,如果你在flatMap()中使用subscribeOn()/observeOn(),它隻影響你建立的flatMap()中的Observable,而不是整個流。
  2. 當你建立一個Observable流並且內聯了一堆操作符以後,compose()會立即執行,flatMap()則是在onNext()被呼叫以後才會執行,換句話說,flatMap()轉換的是每個專案,而compose()轉換的是整個流。
  3. flatMap()一定是低效率的,因為他每次呼叫onNext()之後都需要建立一個新的Observable,compose()是操作在整個流上的。

如果你想用可重用的程式碼替換一些操作符,可以利用compose()和flatMap(),但他們不是唯一的解決辦法。

相關文章