Protocol BuffersでAPI通信 Android編

## protocol buffersとは
API通信などで、Jsonのようなテキスト形式で通信を行わずに、protocol buffersはバイナリ形式で通信を行います。
proto file(事前にスキーマを定義するファイル)から、サーバー側とクライアント側に、シリアライズ・デシリアライズ用のファイルを書き出し、それらを利用してデータの送受信を行います。

## proto fileの定義

今回は都道府県のリストを返すAPIを作成して行きたいと思います。
まず、以下のようにproto fileを定義して行きいます。

```protobuf:prefectures.proto

syntax = "proto3";

option java_package = "com.takusemba.gouda.protos";
option java_outer_classname = "Prefectures";
option go_package = "proto";

package prefectures;

message Prefecture {
  int64 id                   = 1; // ID
  string name                = 2; // 名前
  string romaji              = 3; // 読み方（ローマ字）
}

message GetPrefecturesResponse {
  repeated Prefecture prefectures = 1;
}

```

実際のprotoのコードは[こちら](https://github.com/TakuSemba/cheddar/blob/master/protos/prefectures.proto)に上がっています。

## Protoからjavaのfileを書き出す

先ほど定義したproto fileから、client側のシリアライズ・デシリアライズ用のファイルを書き出します。

こちらのsquare製のwireを使って書き出していきます。
https://github.com/square/wire

read.meからjarファイルをダウンロードして、protoを定義したフォルダに入れます。

その後以下のコマンドでjavaファイルが書き出せます。

```bash

java -jar wire-compiler-VERSION-jar-with-dependencies.jar \
    --proto_path=your/proto/path \
    --java_out=your/output/path
```


実行すると[このような](https://github.com/TakuSemba/cheddar/tree/master/java/com/takusemba/gouda/protos)ファイルが書き出されると思います。

## ProtoでAPI通信を行う

##### 必要なライブラリをgradleに追加

```
  // retrofit
  compile "com.squareup.retrofit2:retrofit:$retrofitVersion"
  compile "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion"
  compile "com.squareup.retrofit2:converter-wire:$retrofitVersion"
  compile "com.squareup.okhttp3:logging-interceptor:$okhttpVersion"
  compile "com.squareup.okhttp3:okhttp:$okhttpVersion"
```

##### retrofitのclientを定義
今回はkotlinを使って書いていきます。

```kotlin


private val okHttpClient: OkHttpClient = OkHttpClient
            .Builder()
            .addNetworkInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(15, TimeUnit.SECONDS)
            .writeTimeout(15, TimeUnit.SECONDS)
            .build()


private val protoRetrofit: Retrofit = Retrofit
            .Builder()
            .baseUrl(endpoint)
            .client(okHttpClient)
            .addConverterFactory(WireConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .build()

```


##### インターフェースを定義

レスポンスの型は生成されたptoroの型(ここではGetPrefecturesResponse)にします。

```kotlin

    interface Service {

        @GET("prefectures") fun getProtoPrefectures(): Single<GetPrefecturesResponse>
    }

```


##### APIを叩く

```kotlin

fun getProtoPrefectures(): Single<List<Prefecture>> {
        return protoRetrofit
                .create(Service::class.java)
                .getProtoPrefectures()
                .map(GetPrefecturesResponse::prefectures)
                .map {
                    val prefectures: ArrayList<Prefecture> = ArrayList()
                    it.mapTo(prefectures) { value ->
                        Prefecture().apply {
                            id = value.id
                            name = value.name
                            romaji = value.romaji
                        }
                    }
                    prefectures.toList()
                }
    }


//APIを叩く
apiClient.getProtoPrefectures()
                    .doOnSubscribe({ disposables.add(it) })
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(
                            {
                                Timber.d("success: " + it?.toString())
                            },
                            {
                                Timber.d("error: " + it.message)
                            }
                    )

```

実際のコードは、こちらの[レポジトリ](https://github.com/TakuSemba/gouda)
で公開しているので、ぜひ見て見てくだい。