de[v|b]log

ShellScript, Coffee, iOS/OSX Dev
Origin: Himajinworks.
About.

square/retrofit を使って簡単なAPIクライアントを書いた。

使う必要性があったため、調査も兼ねて square/retrofit を用いて簡単なAPIクライアントを書いていた。簡単な流れについてまとめる。

尚、対象のAPIは niconico コンテンツ検索API の特にVideo検索とした。 以下の例のコードを実行する( MovieInfoFetcher.fetchMovieInformation() )と、投稿が新しい順で、動画のタイトルと投稿日時が表示される。

コード群

NiconicoAPIClient.java

なぜシンタックスハイライトがうまく効かないかはまた別の話

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package retrofit.example.himajinworks.net.apiclient;

import java.io.IOException;

import retrofit.example.himajinworks.net.model.Video;
import retrofit.example.himajinworks.net.model.NicoResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.Retrofit;
import retrofit2.http.GET;
import retrofit2.http.Query;

/*
  今回Videoだけに限定したため、簡単な感じになっている。
 */
public final class NiconicoAPIClient {
  // BaseURLを指定しておく
  public static final String API_BASEURL = "http://api.search.nicovideo.jp/api/v2/";

  // URIのパスを @Path() で、クエリを @Query() で構築する
  // @Query の場合は @Path とは異なり、プレースホルダで指定する必要はなかった
  public interface NicoVideo {
    @GET("video/contents/search")
    Call<NicoResponse<Video>> response(
      @Query("q") String query,
      @Query("targets") String targets,
      @Query("fields") String fields,
      @Query("_sort") String sort,
      @Query("_offset") int offset,
      @Query("_context") String context
    );
  }

  // 動画の取得の例を示す
  public static void newVideos(
    String query,
    Callback<NicoResponse<Video>> callback
  ) throws IOException {

    // Retrofitのインスタンスを構築する
    // ここではGSONを使ってレスポンス(JSON)のパースを行っている
    Retrofit retrofit = new Retrofit.Builder()
      .baseUrl(API_BASEURL)
      .addConverterFactory(GsonConverterFactory.create())
      .build();

    // パラメータを構築する
    NicoVideo nicovideo = retrofit.create(NicoVideo.class);
    Call<NicoResponse<Video>> call = nicovideo.response(
      query,
      /* targets = */ "title,description,tags",
      /* fields  = */ "title,contentId,startTime,viewCounter",
      /* _sort   = */ "-startTime",
      /* offset  = */ 0,
      /* context = */ "sampleApp"
    );

    // 取得処理を行う
    call.enqueue(callback);
  }
}

NicoResponse.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package retrofit.example.himajinworks.net.model;

import java.util.List;

/**
 * Created by kmhjs on 6/1/16.
 */

public class NicoResponse<ServiceType extends NicoServiceModel> {
  public final Meta meta;
  public final List<ServiceType> data;

  // JSONの構造が複雑な場合でもこんな感じでいい感じになる
  public NicoResponse(Meta meta, List<ServiceType> data) {
    this.meta = meta;
    this.data = data;
  }
}

NicoServiceModel.java

1
2
3
4
5
6
7
package retrofit.example.himajinworks.net.model;

/**
 * Created by kmhjs on 6/1/16.
 */
public class NicoServiceModel {
}

Meta.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package retrofit.example.himajinworks.net.model;

/**
 * Created by kmhjs on 6/1/16.
 */

public class Meta {
  public final int status;
  public final int totalCount;
  public final String id;

  public Meta(int status, int totalCount, String id) {
    this.status = status;
    this.totalCount = totalCount;
    this.id = id;
  }
}

Video.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package retrofit.example.himajinworks.net.model;

/**
 * Created by kmhjs on 6/1/16.
 */
public class Video extends NicoServiceModel {
  public final String title;
  public final String contentId;
  public final String startTime;
  public final int viewCounter;

  public Video(String title, String contentId, String startTime, int viewCounter) {
    this.title = title;
    this.contentId = contentId;
    this.startTime = startTime;
    this.viewCounter = viewCounter;
  }
}

MovieInfoFetcher.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package retrofit.example.himajinworks.net.retrofitexample;

import java.io.IOException;

import retrofit.example.himajinworks.net.apiclient.NiconicoAPIClient;
import retrofit.example.himajinworks.net.model.Video;
import retrofit.example.himajinworks.net.model.NicoResponse;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

/**
 * Created by kmhjs on 6/1/16.
 */
public class MovieInfoFetcher {
  public static void fetchMovieInformation() {
    try {
      // クエリを「ゲーム」として検索する例
      NiconicoAPIClient.newVideos("ゲーム", new Callback<NicoResponse<Video>>() {
        // 正しいレスポンスが返ってきた(正常)の場合
        @Override
        public void onResponse(
          Call<NicoResponse<Video>> call,
          Response<NicoResponse<Video>> response)
        {
          if (!response.isSuccessful()) {
            System.out.println(response.message());
            System.out.println(response.code());
            return;
          }

          // response.body() にはCall<T>で与えた型パラメータTのインスタンスが入っているので
          // それを扱えるようになっている
          NicoResponse<Video> res = response.body();
          for (Video video : res.data) {
            System.out.println(video.title + " uploaded at " + video.startTime);
          }
        }

        // 404等のエラーが返ってきた場合
        @Override
        public void onFailure(Call<NicoResponse<Video>> call, Throwable t) {
          System.out.println("Connection failure");
        }
      });
    } catch (IOException e) {
      System.out.println("Caught IOException" + e.getLocalizedMessage());
    }
  }
}

build.gradle

1
2
3
4
5
6
...

    compile 'com.squareup.retrofit2:retrofit:2.0.2'
    compile 'com.squareup.retrofit2:converter-gson:2.0.2'

...

参考にしたページ