使用 RxJava 封裝現有的庫

zlx36w71 8年前發布 | 9K 次閱讀 RxJava Android Android開發 移動開發

RxJava 是最近 Android 世界里十分流行的一個庫,并且有著充分的流行的理由。雖然函數式響應編程的學習曲線十分陡峭,但學會之后的好處是相當巨大的。

我曾遇到的一個問題是我需要使用一個不支持 RxJava,而是使用了監聽模式的庫,因此無法享受Rx的很多在可組合性方面的便利。

我碰到這個實際問題是在集成 OpenIAB 至最新版本的 Fragment 時。更困難的是,OpenIAB 使用startActivityForResult來啟動一個新的 Activity 并返回一個結果。這使我開始思考,如何將 OpenIAB 和 RxJava 結合在一起使用呢?

將它封裝起來

解決方案是將現有的庫用 Rx 封裝起來。這實際上非常簡單,并且這些基本的原則能應用于任何基于監聽器的庫。

如果你的庫擁有可用的同步方法,那么將其用 RxJava 封裝起來的最好的方式是使用Observable.defer()。這會簡單地延遲這個調用直到 observable 被訂閱,然后在 subscription 的分配線程中執行。

    public Observable
         wrappedMethod() {
          return Observable.defer(() -> {
            return Observable.just(library.synchronousMethod());
          });
        }

這是迄今為止最簡單的封裝現有的庫的方法。并且好于使用庫的監聽器,因為那種混雜在不同線程中的處理方式會令人感到困惑。

在某些情況下,比如 OpenIAB 中,不是所有的方法都支持寫成同步的調用。這時候,我們就必須使用一些不同的方法來封裝這個庫了。

API

我喜歡由外而內地構建一個庫1,因此我們首先需要定義我們的 API。

public interface InAppHelper {

  /**
   * Sets up the InAppHelper if it hasn't been already.
   */
  Observable setup();

  /**
   * Returns the Inventory based on the supplied skus.
   */
  Observable queryInventory(List skus);

  /**
   * Begins the purchase flow for the specified sku.
   */
  Observable purchase(String sku);
}

這三個方法在 OpenIAB 中的基本實現有些小小的不同。setup()使用了一個標準的回調接口,queryInventory()能同步使用,但會拋出一個必須被 catch 的異常,purchase()使用了一個監聽器,但也依賴于startActivityForResult。

讓我們分別看看如何用 RxJava 中的 Observable 封裝這幾種類型的方法。

溫馨小貼士

我在代碼示例中使用了 Java 8的 lambdas 語法來使代碼看起來更簡潔,但我并未將它用在工作中。如果誰非想在工作中使用它,可以使用開源項目Retrolambda,恩,不用謝。

用 RxJava 封裝帶有監聽器的方法

封裝那些使用了監聽器的方法時,Observable.just()并不管用,因為它一般沒有返回值。我們必須使用Observable.create(),這樣我們就可以將監聽器的結果回調給 subscriber。

public Observable setup() {
  return Observable.create(subscriber -> {
    if (!helper.setupSuccessful()) {
      helper.startSetup(result -> {
        if (subscriber.isUnsubscribed()) return;

        if (result.isSuccess()) {
          subscriber.onNext(null);
          subscriber.onCompleted();
        } else {
          subscriber.onError(new IabException(result.getMessage()));
        }
      });
    } else {
      subscriber.onNext(null);
      subscriber.onComplete();
    }
  });
}

一步一步地看上面的代碼,你會發現我們可以在setup()方法中使用Observable.create()創建一個 Observable,并在OnSubscribe代碼塊(譯者注:即 lambda 表達式subscriber -> {}中的代碼)中調用我們基于監聽器的方法。在這些代碼中,我們實現自己的監聽器,并將結果傳給相應的 subscriber。

具體到這個示例中,我們在 OnSubscribe 類中調用helper.startSetup()方法,通過我們自己實現的OnIabSetupFinishedListener將結果傳遞給相應的 subscriber。

由于監聽器總是會被調用,而不管 subsriber 還是否需要,我們必須先調用subscriber.isUnsubscribed()檢查一下,以此來避免發送不必要的消息。

注意,如果通過檢查helper.setupSuccessful()發現 helper 已經設置好了,我們可以輕松地避免調用消耗巨大的startSetup().比如在這個示例中,我們就可以直接調用subscriber.onNext()。

封裝拋出異常的同步方法

第二個我們必須實現的方法是queryInventory(),它能被同步調用,但我們不能使用Observable.just()方法,因為它拋出的IabException并不是RuntimeException的子類,因此必須被捕獲。

我們可以很輕松地用Observable.defer()來解決這個問題。我們將同步調用的代碼用 try-catch 包起來,并根據結果返回Observable.just()或是Observable.error().

public Observable queryInventory(final List skus) {
  return Observable.defer(() -> {
    try {
      return Observable.just(helper.queryInventory(skus));
    } catch (IabException e) {
      return Observable.error(e);
    }
  });
}

這是一個非常簡單的例子。有點需要注意的是返回Observable.error()并不是最好的方法。如果這個異常是可以接受的,那你需要返回一個有用的帶有值的 Observable。記住,onError()只能在 subscription 不再有用時被調用。

封裝使用了監聽器和 Activity Results 的方法

最后一個我們需要實現的方法,purchase(),和上面監聽器的示例類似,但它因為使用了startActivityForResult而更為復雜。由于這里同樣使用了監聽器,因此并不改變我們的 Observable 實現,我們只需要在我們的 Helper 接口里增加一個方法,以便通過它返回 activity 的結果。

由于這和第一個監聽器的例子類似,我們直接來看 OpenIAB 的實現。

public Observable purchase(final String sku) {
  return Observable.create(subscriber -> {
    helper.launchPurchaseFlow(activity, sku, REQUEST_CODE_PURCHASE, (result, info) -> {
      if (subscriber.isUnsubscribed()) return;

      if (result.isSuccess() || result.getResponse() == IabHelper.BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED) {
        subscriber.onNext(info);
        subscriber.onCompleted();
      } else {
        subscriber.onError(new InAppHelperException(result.getMessage()));
      }
    });
  });
}

public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
  return helper.handleActivityResult(requestCode, resultCode, data);
}

如你所見,handleActivityResult()方法只是簡單地將結果傳遞給 IabHelper 來處理。如果那個 activity 的結果和我們的請求相匹配,我們創建的監聽器會被調用,然后監聽器再反過來調用我們的 subscriber 方法。

再次強調,我們需要檢查subscriber.isUnsubscribed()來確保還有觀察者需要我們的結果。

Rx無處不在

這些只是幾個簡單的例子來演示如何用 RxJava 將現有的庫封裝起來。這能幫你靈活地在你的 Android 應用中使用函數式響應編程,并享受它的諸多好處。

來自: http://www.jcodecraeer.com//a/anzhuokaifa/androidkaifa/2016/0413/4141.html

 本文由用戶 zlx36w71 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!