Android × Retrofit2 × RxJava(RxAndroid) でHTTP通信

## はじめに
FiNCでAndroidのアプリ開発を担当している南里、通称Bisonと申します。
テーマは開発プロセスなどを含めて何にするか悩んだんですが、やっぱりAndroid周りの技術のお話をしようかなと思います。

## デシリアライズ(シリアライズ)
以下のようなリソースBisonを考えてみましょう。

```json
{
  "id": 1,
  "name": "南里勇気",
  "age": 26
}
```

Android開発をしているみなさんにはおなじみですが、

<b>HTTP通信でサーバーからデータ(リソース)を取得 -> jsonをデシリアライズ -> JavaのObject(POJO)を生成</b>

という場面が多々あるかと思います。(別にAndroidだけってことではないですが)

Androidだと、シリアライズ、デシリアライズのサードパーティライブラリとしては、[Gson](https://github.com/google/gson/tree/master/gson) などでしょうか？

最近の流行りでいうと、

<b>RxJava</b>, <b>Retrofit</b> などを利用しますよね。


そこで本日は、

<b>・RxJava × Retrofit2のHttp Clientの作成<b>
<b>・リソースのデシリアライズ(シリアライズ)方法<b>


について簡単にまとめます。

## RxJava × Retrofit2 Http Clientの作成
### Steps
1. Retrofit Clientを作成する
2. Serviceの作成。(Observable)

#### 1. Retrofit Clientを作成する
まず、[Retrofit](https://github.com/square/retrofit)とは、Squareで作成されたAndroid(Java)のためのHttp Client ライブラリのことです。
(SquareはAndroid業界では圧倒的戦闘力をもつJake Whartonが所属しています。)

まずRetrofit Clientを作成します。
(余談ですが、Retrofit -> Retrofit2へのアップグレードは相当breakingでしたね)

内部はBuilderパターンで実装されており、

```java
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.bison.com")
    .client(okhttpClient);
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .build();
```

こんな感じで簡単に実装できます。

RxJavaを利用するためには、`.addCallAdapterFactory(RxJavaCallAdapterFactory.create())` この一行が必要になるので、お忘れなく。
また、Httpのheader情報、その他のオプション情報をセットしたい場合には、Okhttp3のInterceptorという仕組みを利用します。


```java
this.headerInterceptor = new Interceptor() {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Request.Builder newBuilder = request.newBuilder();
        newBuilder.addHeader("Client-Type", "bison");
        final Request newRequest = newBuilder.build();
        return chain.proceed(newRequest);
    }
};
```

Okhttp Clientに上記をセットして使ってください。

#### 2. Serviceの作成。(Observable)

上記まで完了すれば、あとはend pointを設定しましょう。
詳しくは、この辺り読んでみてください。
http://square.github.io/retrofit/2.x/retrofit/retrofit2/http/GET.html

```java
public interface BisonApiService {
    // HTTPリクエストのメソッド、エンドポイントのセット
    @GET("v1/bisons/{bison_id}")
    Observable<Bison> getBison(@Path("bison_id") int bisonId);
    
}
```

あとは、

```java
retrofit.create(BisonApiService.class)
    .getBison(100);
```

とすれば、interfaceに定義した、 `Observable<Bison>` が取得できます。


あとはsubscribeして、Bisonモデルをのデータを取得すれば、完了です。
RxJavaを利用したObservableの扱いは[非同期や並列処理にも役立つRxJavaの使い方](http://qiita.com/disc99/items/1b2e44a1105008ec3ac9)などがとても参考になるかと思います。


次は、Bisonのデータの取得に関してです。
ここから本題のデシリアライズに入りましょう。

a. Gsonの設定
b. APIのレスポンスをMappingするModelを作成する。


a. デシリアライズしたい際には、以下のように別モジュールにあるGsonConverterをセットします。


```java
Gson gsonInstance = new GsonBuilder()
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .create();

new Retrofit.Builder()
    .addConverterFactory(GsonConverterFactory.create(gsonInstance));
```


b. APIのレスポンス用モデルの作成(シリアライズ、デシリアライズ両用)

```java
public class Bison{

    @SerializedName("id")
    public final int id;
    
    @SerializedName("name")
    public final String name;
    
    @SerializedName("age")
    public final int age;
　　　　
    // コンストラクタ  
}
```

これでGsonが上手にデシリアライズ、シリアライズ(post, patchなどもOK)してくれます。
ただ、実際に開発をしていると次のような問題にぶち当たると思います。

例えば、以下のようにデータ構造に差異がある場合です。

```json
{
  "page": "1",
  "data": [
    {
      "type": "bison", 
      "id": 1,
      "name": "南里勇気",
      "age": 26
    }, 
    {
      "type": "snake", 
      "id": 3,
      "name": "viper"
    }
  ]
}
```

上記のパターンは、ページングを行うタイムライン系のサービスではよくあります。

その場合、`Observable<List<特定のクラス>>` みたいにしたくなりますよね？
蛇もバイソンもアニマルなので、Animalクラスを定義して継承して、 `Observable<List<Animal>>` にしたくなります。
ところが、それぞれの具象クラス(Bison, Snake)にデシリアライズしなければ、上記のようにList<Animal> のデータを有効活用できません。
なので、具象クラスにパースする必要があります。

その場合には、GsonのJsonDeserializerを実装したカスタムクラスを作成します。

```java
public class AnimalDeserializer
        implements JsonDeserializer<Animal> {

    @Override
    public Animal deserialize(JsonElement json, Type typeOfT,
                                        JsonDeserializationContext context) throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();
        final JsonElement jsonActivityType = jsonObject.get("type");
        // 上記のキー("type")はアプリケーション内で一意性を保つ必要があります。(通信の際には常にこのDeserializerがregistされているため)
        final String type = jsonActivityType.getAsString();

        switch (type) {
            case "bison":
                return context.deserialize(json, Bison.class);
            case "snake":
                return context.deserialize(json, Snake.class);
            default:
                return context.deserialize(json, Animal.class);
        }
    }
}
```

あとは、上記でも記載しているGsonBuilderに部分で、以下を追加すると完成です。

```java
new GsonBuilder().registerTypeAdapter(Animal.class, new AnimalDeserializer());
```

これで、RxJava × Retrofit2での適切なデシリアライズ(シリアライズ)が可能になりました。
軽く調べた感じでは、あまり検索にヒットしなかったので、一人でも多くのAndroidエンジニアを救えたらと願うそんな12/7なのでした。