tumblr

tumblr(タンブラー)は、メディアミックスブログサービス。ブログとミニブログ、そしてソーシャルブックマークを統合したマイクロブログサービスである。アメリカのDavidville.inc(現: Tumblr, Inc.)により2007年3月1日にサービスが開始された。

retrofitでAPIを楽に使う

androidから何かサーバ側のAPIを叩く場合、色々と用意しなければならない。OkHttpなどのHTTP通信を行うインスタンス、通信は基本非同期処理なのでコールバックを渡すためのラッパー、それにAPIのURLとパラメータのチェック、APIから返ってきた値をコンバートするためのものなど…それらの面倒事を一手に引き受けてくれるのがretrofitだ。

概要

retrofitはHTTPクライアント。javaのインターフェース形式でAPIを定義出来る。こんな風に。

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

/users/{user}/reposというパスに対してGETアクセスをするというメソッドがこれだけで定義できる。{user}は可変のユーザー名。userに入る引数とともにこのlistReposというメソッドを呼んであげればそれでHTTPGETが出来る。返り値はRepoクラスのインスタンスのListとなる。あとはbuilderを使ってこのインターフェースを実装したインスタンスを作ればオールOK。

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

Call<List<Repo>> repos = service.listRepos("octocat");

当然GETだけでなくPOSTやPUTやDELETEも可能。formEncordedなものや画像データ、マルチパートも投げることができる。 内部的にはOkHttpやVolleyなどをクライアントとして利用することができる。なのでOkHttpに噛ませたインターセプターなどともバッティングすることなく利用することができる。 また、レスポンスはjacksonやgsonなどのコンバータがサポートされているので、サポートされているもののうち好きなコンバータを使えばレスポンスjson=>javaオブジェクトへの変換も勝手にやってくれる。

導入

android studioでgradleを使っているなら以下をいれてsyncすればOK。

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'

バージョンは2をしていしたほうがいいだろう。それまではバージョン1.9がメインだったものの、夏ごろにメジャーバージョンがあがった。で、メジャーバージョンがあがったのにつれてインターフェースの宣言の仕方などがかなり変わったようだし、今のところは2.0に関しての情報も十分にあるので1.9を選ぶ必要はない。以降はバージョン2.0での使い方を説明する。

jsonコンバーターを指定する場合はそれ用のライブラリも必要となることに注意。自分はjacksonを使っているので、以下も一緒にbuild.gradleに書き加えてある。

compile 'com.squareup.retrofit:converter-jackson:2.0.0-beta2'

使ってみよう

使う前にはまずレスポンスを格納するEntityクラスが必要なのでそれを作っておく。jacksonをつかっているので、jackson形式でのEntityを定義している。ここは適宜gsonなどに変えればいい。

public class Repo {
 
    public int id;
    public String name;
    public String url;
    public boolean private;
 
}

そして重複するがインターフェースの宣言。

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

あとはこれを呼ぶ。

public class GithubActivity extends AppCompatActivity {

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

        Retrofit retrofit = new Retrofit.Builder()
              .baseUrl("https://api.github.com")
              .build();

         GitHubService service = retrofit.create(GitHubService.class);
         Call<List<Repo>> call = service.listRepos("shim0mura");
         call.enqueue(new Callback<List<Repo>>() {
              @Override
              public void onResponse(Response<List<Repo>> response) {
                     if (response.isSuccess()) {
                         List<Repo> result = response.body();
                     }
             }
 
              @Override
              public void onFailure(Throwable t) {
 
              }
         });
    }

}

serviceという変数にretrofitのインターフェースを実装したインスタンスをいれる。そしてそれから定義したメソッドを呼ぶ。そのメソッドの返り値にenqueueしたところで通信を始める。enqueueメソッドの引数はコールバック。通信が終了したところでコールバック内のメソッドが実行される。基本的に20x系のレスポンスでも50xや40xなどのエラー系のレスポンスでも全部onResponseが呼び出される。なので20x系の成功時のレスポンスを判定したいときはisSuccessメソッドで判定を行う。あとはonResponseの仮引数、この場合はresponseというインスタンスのbodyメソッドを呼べば、jsonなどの生のレスポンスは先ほど定義したEntityクラスインスタンスに勝手にコンバートしてくれる。楽ちん。

enqueueを使わずにcall.execute()とすれば非同期でない同期的な通信もできるらしい。使ったこと無いけど…

POSTも出来る

GET以外については以下のようにメソッドを定義する。

@POST("/user")
    Call<User> createUser(
            @Body HashMap<String, User>user
    );

@Bodyアノテーションでpostしたいオブジェクトを指定すればいい。呼び出し時も

new HashMap<String, User> postUser = new HashMap<>();
User user = new User();
user.name = "shim0mura";
user.job ="unemployed";
postUser.put("user",  user);
Call<User> call = service.createUser(postUser);

とGETとほとんど変わることなく呼び出せる。enqueueなどのコールバック処理も全く同じ。インターフェース側だけ変えればいいだけなのである。 ちなみにわざわざHashMapを渡さず

@POST("/user")
    Call<User> createUser(
            @Body User user
    );

と直にEntityを渡す事もできる。というかそっちの方が普通な使い方。じゃあ何故HashMapなんか使ったのかというと、サーバ側でparams[:user][:name]というrailsっぽい形式で取得したかったため。後者のような普通なpostだとparams[:CreateUser]の中にnameやjobが入っているという形式になってしまう。そのままだとどうやらメソッド名(しかもアッパーキャメル)で内容がpostされてしまう。HashMapを使えば、hashのキーがpost内容のキーになるのでrails使いには楽。

OkHttpを使う

更に、retrofitの内部のHTTPクライアントとしてOkHttpやVolleyなどを使うこともできる。 使い方というか指定の仕方は簡単。

OkHttpClient client = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder()
              .baseUrl("https://api.github.com")
              .client(client)
              .build();

これだけ。buildの際にclientメソッドを呼び出し、その引数としてOkHttpのインスタンスを入れればいいだけ。なのでそのインスタンスをclientに入れる前にインターセプターを噛ましていても問題ない。

OkHttpClient client = new OkHttpClient();
client.interceptors().add(new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
 
        // Do anything with response here
 
        return response;
    }
});

Retrofit retrofit = new Retrofit.Builder()
              .baseUrl("https://api.github.com")
              .client(client)
              .build();

これでちゃんとインターセプターは動作する。

コンバータの指定

更に更にコンバータもHTTPクライアントと同じようにして指定することができる。

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://api.nuuneoi.com/base/")
        .addConverterFactory(JacksonConverterFactory.create())
        .build();

これだけ。コンバータをgsonに変えたくなったら、GsonConverterFactory.create()に変えてやればいい。(もちろんbuilde.gradleでgsonコンバータを加えている前提)

という感じで自前の面倒なコンバータやAPIラッパーを作らなくてもretrofitさえあれば大体のことはまかなえる。素敵。

square.github.io