blog

AndroidでRxJavaを使ってREST APIクライアントを作る

Androidの開発をしたことがある人なら、Netflixが開発したレスポンシブ拡張機能のJava実装であるRxJavaのことを聞いたことがあるかもしれません。MSDNからその定義を引用すると、Rea...

Jan 6, 2014 · 11 min. read
シェア

Androidの開発をしたことがある人なら、Netflixが開発したReactive ExtensionsのJava実装であるRxJavaについて聞いたことがあるかもしれません。引用すると、Reactive Extensionsは、非同期およびイベントベースのプログラミングのために、observableコレクションとLINQスタイルのクエリを組み合わせたサードパーティライブラリです。

必要なライブラリファイルを追加することから始めます。Mavenを使用している場合は、以下の依存関係をpom.xmlに追加するだけです:

<dependency> 
    <groupId>com.squareup.retrofit</groupId> 
    <artifactId>retrofit</artifactId> 
    <version>1.2.2</version> 
</dependency> 
<dependency> 
    <groupId>com.netflix.rxjava</groupId> 
    <artifactId>rxjava-android</artifactId> 
    <version>0.14.6</version> 
</dependency> 

この記事では、OpenWeatherMap API をデモ例として使用します。 OpenWeatherMapフリーの気象データAPIで、設定や利用が非常に簡単です。呼び出す際には、位置情報をパラメータとして渡すだけでよく、具体的な効果についてはこの例を参照してください。デフォルトではJSON形式でデータを転送します。精度と温度の単位も設定可能です。詳細はこちらご覧ください。

通常、APIの呼び出しを実装するには、以下の手順が必要です:

  1. 必要なモデルクラスを作成します。
  2. エラー処理を伴うリクエスト・レスポンス管理を実装するネットワーク層のコード。
  3. リクエスト・コールを実装するためにバックグラウンド・スレッドが使用され、UIスレッドでレスポンス・メッセージを表示するためにコールバック関数が使用されます。

モデルクラスの作成

*** 同様のjsonschema2pojoJSON-POJO 生成ツールに頼ることができます:

public class WeatherData { 
  
    public Coordinates coord; 
    public Local sys; 
    public List<Weather> weathers; 
    public String base; 
    public Main main; 
    public Wind wind; 
    public Rain rain; 
    public Cloud clouds; 
    public long id; 
    public long dt; 
    public String name; 
    public int cod; 
  
    public static class Coordinates { 
        public double lat; 
        public double lon; 
    } 
  
    public static class Local { 
        public String country; 
        public long sunrise; 
        public long sunset; 
    } 
  
    public static class Weather { 
        public int id; 
        public String main; 
        public String description; 
        public String icon; 
    } 
  
    public static class Main { 
        public double temp; 
        public double pressure; 
        public double humidity; 
        public double temp_min; 
        public double temp_max; 
        public double sea_level; 
        public double grnd_level; 
    } 
  
    public static class Wind { 
        public double speed; 
        public double deg; 
    } 
  
    public static class Rain { 
        public int threehourforecast; 
    } 
  
    public static class Cloud { 
        public int all; 
    } 
  
} 

レトロフィットによるネットワーク・コールの実施

ネットワークコールの実装の2番目のステップは、通常、多くの定型的なコードを記述する必要がありますが、あなたがそれを達成するためにSquareのRetrofit使用すると、コードの量を大幅に削減します。インターフェースクラスを作成し、RestAdapter.Builderを使ってライン上にクライアントを作成するだけです。 Retrofitは、JSONのシリアライズとデシリアライズを完了するためにも使えます。

private interface ApiManagerService { 
    @GET("/weather") 
    WeatherData getWeather(@Query("q") String place, @Query("units") String units); 
} 

具体的な呼び出しの例を見てください。このコードはまだ比較的簡単に理解できます:

//... 
final RestAdapter restAdapter = new RestAdapter.Builder() 
    .setServer("http://..org/data/.5") 
    .build(); 
  
final ApiManagerService apiManager = restAdapter.create(ApiManagerService.class); 
final WeatherData weatherData = apiManager.getWeather("Budapest,hu", "metric"); 
//... 

コールプロセス全体を実装するのに必要なコードはほとんどありません。

RxJavaによるレスポンシブプログラミング

次はステップ3、RxJavaの部分です!この例では、非同期リクエストコールを実装するためにRxJavaを使用します。以下のRxJavaの紹介はNetflixのGithubナレッジベースから引用しています:

RxJavaはJava仮想マシン上に実装されたレスポンシブ拡張ライブラリで、観測可能なシーケンス実装とイベントベースのプログラミングに基づく非同期コールを提供します。

Observerパターンを拡張してデータ、イベントシーケンスをサポートし、基礎となるスレッド処理、同期、スレッド安全性、並行データ構造、ノンブロッキングI/O処理を気にすることなくシーケンスをマージすることができます。

Java5以降をサポートし、Groovy、Clojure、Scalaなど他のJVMベースの言語も多数サポートしています。

すでにRxJavaの知識があることを前提としています。そうでない場合は、まずこちらNetflixのGithub Wikiの最初の数ページをチェックすることを強くお勧めします。

この***の例では、観測可能なオブジェクトを生成し、複数の同時呼び出しを行うAPIマネージャが実装されます。

まず、先ほど作成したインターフェイス・クラスをこのクラスに置き換える必要があります:

public class ApiManager { 
  
    private interface ApiManagerService { 
        @GET("/weather") 
        WeatherData getWeather(@Query("q") String place, @Query("units") String units); 
    } 
  
    private static final RestAdapter restAdapter = new RestAdapter.Builder() 
        .setServer("http://..org/data/.5") 
        .build(); 
    private static final ApiManagerService apiManager = restAdapter.create(ApiManagerService.class); 
  
    public static Observable<WeatherData> getWeatherData(final String city) { 
        return Observable.create(new Observable.OnSubscribeFunc<WeatherData>() { 
            @Override 
            public Subscription onSubscribe(Observer<? super WeatherData> observer) { 
                try { 
                    observer.onNext(apiManager.getWeather(city, "metric")); 
                    observer.onCompleted(); 
                } catch (Exception e) { 
                    observer.onError(e); 
                } 
  
                return Subscriptions.empty(); 
            } 
        }).subscribeOn(Schedulers.threadPoolForIO()); 
    } 
  
} 

getWeatherData()メソッドを見てみましょう。Observable.create()を呼び出し、Observable.OnSubscribeFuncの実装を渡してObservableオブジェクトを取得し、それを返すメソッドで、Observableがサブスクライブされると動作を開始します。OnSubscribeFuncの実装でObservableオブジェクトを取得し、それを返すメソッドで、Observableオブジェクトがサブスクライブされると動作を開始し、Observableの各処理の結果がonNext()メソッドのパラメータとして渡されます。ここではネットワークリクエストの同時呼び出しを実装しようとしているだけなので、それぞれのObservableオブジェクトがリクエストを1回呼び出すだけでよいのです。Code*** は onComplete() メソッドを呼び出します。subscribeOn()メソッドは、アプリケーションがどのスレッドを選択するかを決定するため、ここでは重要です。Schedulers.threadPoolForIO()がここで呼び出されます。このスレッドは、IOとネットワークパフォーマンスに関連するタスクを最適化するために使用されます。

*** 最初のステップは、このAPIコールを実装することです。以下のコードでは、それぞれ異なるコールパラメータを使用して同じ url を非同期にコールする、同時 Web リクエストを実装しています:

Observable.from(cities) 
            .mapMany(new Func1<String, Observable<WeatherData>>() { 
                @Override 
                public Observable<WeatherData> call(String s) { 
                    return ApiManager.getWeatherData(s); 
                } 
            }) 
            .subscribeOn(Schedulers.threadPoolForIO()) 
            .observeOn(AndroidSchedulers.mainThread()) 
            .subscribe(new Action1<WeatherData>() { 
                @Override 
                public void call(WeatherData weatherData) { 
                    // do your work 
                } 
            }); 

Observable.from()メソッドは都市名の配列をobservableオブジェクトに変換し、配列内の文字列を異なるスレッドに提供します。mapMany()メソッドは、前者によって提供された各文字列をobservableオブジェクト()に変換します。ここでの変換はApiManager.getWeatherData()を呼び出すことで行われます。

ここではまだI/Oスレッドプールに登録されています。Androidでは、結果をUIに表示する必要がある場合、データをUIスレッドにポストして処理する必要があります。Androidでは、UIを作成した元のスレッドだけがUIを操作できることがわかっているからです。ここでは、observeOn()メソッドでAndroidSchedulers.mainThread()を呼び出すだけです。subscribe()メソッドの呼び出しがobservableオブジェクトのトリガーとなり、observableオブジェクトが発行した結果をここで処理できます。

この例はRxJavaの威力を示しています。Rxがなければ、リクエストを呼び出すためにN個のスレッドを作成し、その結果を非同期でUIスレッドに渡す必要があります。Rxを使えば、ほとんどコードを書かずに、Rxのパワーを使ってobservableオブジェクトを生成、結合、フィルタリング、変換することができます。

RxJavaは、Androidアプリを開発する際に並行処理を行うための強力なツールとして使用できます。使いこなすにはまだ時間がかかりますが、一度使いこなせば、ナイフを研ぐことが大いに役立ちます。Responsive Extensionsライブラリは素晴らしいアイデアで、Androidアプリ開発に使うのは数週間前から良いアイデアになっています。知れば知るほど好きになるはずです。

他の情報も必要ですか?RxJavaがどのようにエラー処理を行うかについてはご覧ください。

Read next

OAシステムはどのように全体の状況を活性化させることができる詳細を持つ

現在、現代的な経営モデルを持つ企業であれば、市場化と国際化に向かう過程で、無視できない主要な問題は、企業規模が徐々に拡大し、社内コミュニケーションや作業効率に関する部門間や役職間の協力が年々増加していることです。

Jan 6, 2014 · 2 min read