12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

FiNC DeveloperAdvent Calendar 2016

Day 7

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

Last updated at Posted at 2016-12-07

はじめに

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

デシリアライズ(シリアライズ)

以下のようなリソースBisonを考えてみましょう。

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

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

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

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

Androidだと、シリアライズ、デシリアライズのサードパーティライブラリとしては、Gson などでしょうか?

最近の流行りでいうと、

RxJava, Retrofit などを利用しますよね。

そこで本日は、

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

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

RxJava × Retrofit2 Http Clientの作成

Steps

  1. Retrofit Clientを作成する
  2. Serviceの作成。(Observable)

1. Retrofit Clientを作成する

まず、Retrofitとは、Squareで作成されたAndroid(Java)のためのHttp Client ライブラリのことです。
(SquareはAndroid業界では圧倒的戦闘力をもつJake Whartonが所属しています。)

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

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

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

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

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

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

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

あとは、

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

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

あとはsubscribeして、Bisonモデルをのデータを取得すれば、完了です。
RxJavaを利用したObservableの扱いは非同期や並列処理にも役立つRxJavaの使い方などがとても参考になるかと思います。

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

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

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

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

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

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

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)してくれます。
ただ、実際に開発をしていると次のような問題にぶち当たると思います。

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

{
  "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 のデータを有効活用できません。
なので、具象クラスにパースする必要があります。

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

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に部分で、以下を追加すると完成です。

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

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

12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?