[Android] OkHTTP + Retrofit + RxAndroid で REST クライアントを実装する

Android

REST 処理を扱いやすく実装する

Android の REST クライアントのネットワーク処理と非同期処理について、最近流行っている OkHTTP と Retrofit と RxAndroid の組み合わせを使ってみました。これらはそれぞれ次のような機能を提供するライブラリです。

OkHTTP

  • HTTP クライアント用のライブラリ
  • HTTP2/SPDY などのプロトコルにも対応
  • HTTP ヘッダーのポリシーに準拠したローカルキャッシュの実装

Retrofit

  • REST クライアント用のライブラリ
  • アノテーションでメソッドやパラメータを指定可能
  • 通信処理は抽象化されているため自由に実装可能

RxAndroid

  • RxJava の Android 版
  • リアクティブプログラミングを Android で実装するためのライブラリ
  • イベントベースで遅延実行や非同期コールバックなどを実装可能

なお、Retrofit が OkHTTP と RxAndroid の架け橋になってくれるため、組み合わせてシンプルに記述可能になります。

導入

まずはこれら3つのライブラリを Android プロジェクトに導入しましょう。

dependencies {
    compile 'com.squareup.okhttp:okhttp:2.4.0'
    compile 'com.squareup.retrofit:retrofit:1.9.0'
    compile 'io.reactivex:rxandroid:0.24.0'
}

実装する

準備が整いましたので、実装していきましょう。OpenWeatherMap の JSON を GET してくるサンプルを実装したいと思います。

Entity クラスの作成

今回は JSON のパーサに Gson を使います。ということでまずはエンティティクラスを作りましょう。以下は余分なプロパティを省略していますのでご容赦ください。

package jp.classmethod.sample.restsample;

import java.util.List;

public class WeatherEntity {

    public String base;
    public List<Weather> weather;

    public class Weather {
        public int id;
        public String main;
        public String description;
        public String icon;
    }

}

REST インターフェースの作成

次に、REST クライアントのインターフェースを作成します。メソッド名を決めてアノテーションを追加するだけです。

package jp.classmethod.sample.restsample;

import retrofit.http.GET;
import retrofit.http.Path;
import retrofit.http.Query;
import rx.Observable;

public interface WeatherApi {

    @GET("/data/2.5/{name}")
    public Observable<WeatherEntity> get(@Path("name") String name, @Query("q") String q);

}

非同期処理を実行する

あとは RestAdapter をインスタンス化し、Observer で Subscribe するだけです。以下は Activity でやっていますがもちろん Fragment でも構いません。

package jp.classmethod.sample.restsample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.internal.bind.DateTypeAdapter;

import java.util.Date;

import retrofit.RestAdapter;
import retrofit.android.AndroidLog;
import retrofit.converter.GsonConverter;
import rx.Observer;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;


public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // JSONのパーサー
        Gson gson = new GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .registerTypeAdapter(Date.class, new DateTypeAdapter())
                .create();

        // RestAdapterの生成
        RestAdapter adapter = new RestAdapter.Builder()
                .setEndpoint("http://api.openweathermap.org")
                .setConverter(new GsonConverter(gson))
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .setLog(new AndroidLog("=NETWORK="))
                .build();

        // 非同期処理の実行
        adapter.create(WeatherApi.class).get("weather", "Tokyo,jp")
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<WeatherEntity>() {
                    @Override
                    public void onCompleted() {
                        Log.d("MainActivity", "onCompleted()");
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e("MainActivity", "Error : " + e.toString());
                    }

                    @Override
                    public void onNext(WeatherEntity weather) {
                        Log.d("MainActivity", "onNext()");
                        if (weather != null) {
                            ((TextView) findViewById(R.id.text)).setText(weather.weather.get(0).main);
                        }
                    }
                });

    }

}

以上で終わりです!アプリを起動してみると Rain と出ました。

retrofit

まとめ

以上、簡単なサンプルでした。

これらのライブラリの開発に参加している JakeWharton さんすごい!と思いました。

参考