非同期通信をVolleyとOkHttpとRetrofitそれぞれで書いた場合の超簡易サンプル

## はじめに

この記事は、DroidKaigi2017で発表した『[いまからはじめるAndroid 6.0対応 〜Android 7.0から8.xを見つめて〜](https://speakerdeck.com/yamacraft/imakarahazimeruandroid-6-dot-0dui-ying)』の補完的な記事になります。

## 概要

Android 6.0から Apache HTTP Clientが廃止されました。特にGoogleが提供していた通信ライブラリ「Volley」がこの影響をモロに受けてしまっています。

自分が前職で関わっていたアプリもほぼ全てがVolleyを利用していました。結局はOkHttpやRetrofitへの移行によって対応を行ったのですが、（特にOkHttpへの）移行は比較的スムーズに移行が進んだこともあり、改めて「非同期通信を各ライブラリで実装した場合」のサンプルを掲載します。

## `useLibrary 'org.apache.http.legacy'` について

[Apache HTTP Client の削除 | Android 6.0 の変更点 | Android Developers](https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html?hl=ja#behavior-apache-http-client)

実は公式から「Apache HTTP Clientをどうしても使いたい場合の応急的な解決策」は用意されています。

とはいえ、あくまでも応急的な解決策に近いものと考えています。公式にいつ削除するとは明言されていないものの、削除しないのであれば最初から除外されるはずがないので、早い段階でApache HTTP Clientを必要としない形への実装に変えるべきだと思います。

ちなみにこの対応を行ったVolleyがGitHubに上がっています。本記事のVolleyサンプルも、こちらのVolleyを利用して実装しています。いずれはこちらもApache HTTP Clientの対応を完了したものになるかもしれないですね。

- [google/volley](https://github.com/google/volley)
- [Transmitting Network Data Using Volley | Android Developers](https://developer.android.com/training/volley/index.html)

## 実装

### Clientクラスによるラッパークラスでの実装について

これが正解！とは口が裂けても言えませんが、移行対象としていたアプリの通信処理は、別途通信処理をラップしたClientクラスに対してやりとりを行うようにする設計をしていました。これによってView側では「どうやってModelを取得するか」を意識しない形となっていたため、変更点も最小限にとどまったような気がしています。

![View-Client.png](https://qiita-image-store.s3.amazonaws.com/0/4560/3c5649dd-d8d4-6b44-d072-c9bda855d986.png "View-Client.png")

本来ならMVPだのMVVMといった「キレイ」な依存関係を作るべきだと思うのですが、いきなり全部を完璧にこなすのは大変だなあと思います。とはいえ、まずはこの程度の「Viewが使うライブラリに依存しない」レベルの分割はやっておいてからOkHttpなどへの移行を進めたほうがスムーズに移行できるのかなと思います。

### サンプルコード

今回は自身が移行に対応したアプリの条件を前提としているので

- GET通信
- JSONが返却されるのでGsonを使ってパース
- 主に非同期通信

という前提で記載しています。あくまでも「そんなに実装方法に違いはないよ」というニュアンスだけ受け取るもので、そのまま考えずにマネないようにしてください。

```java:RequestClient.java
class RequestClient {

    private static final String URL = "https://xxxx.com/get/event/";
    static final int METHOD_VOLLEY = 0;
    static final int METHOD_OKHTTP = 1;
    static final int METHOD_RETROFIT = 2;

    interface Listener {
        void onSuccess(Event event);

        void onFailure();
    }

    private Listener mListener;

    private RequestQueue mRequestQueue; // Volley
    private OkHttpClient mOkHttpClient; // OkHttp

    RequestClient(Context context, Listener listener) {
        this.mListener = listener;
        this.mRequestQueue = Volley.newRequestQueue(context);
        this.mOkHttpClient = new OkHttpClient();
    }

    /**
     * GET通信の実施（取得するURLは固定）
     *
     * @param method 利用するライブラリの種類
     */
    void get(int method) {

        if (method == METHOD_VOLLEY) {
            getVolley(mListener);
            return;
        }

        if (method == METHOD_OKHTTP) {
            getOkHttp(mListener);
            return;
        }

        if (method == METHOD_RETROFIT) {
            getRetrofit(mListener);
            return;
        }

        mListener.onFailure();
    }

    /**
     * Volleyによる非同期GET通信
     *
     * @param listener リスナー
     */
    private void getVolley(final Listener listener) {
        JsonObjectRequest request
                = new JsonObjectRequest(Request.Method.GET, URL, null,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Event event = new Gson().fromJson(response.toString(), Event.class);
                        listener.onSuccess(event);
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        listener.onFailure();
                    }
                });
        mRequestQueue.add(request);
        mRequestQueue.start();
    }

    /**
     * OkHttp3による非同期GET通信
     *
     * @param listener リスナー
     */
    private void getOkHttp(final Listener listener) {
        final okhttp3.Request request = new okhttp3.Request.Builder().url(URL).get().build();
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                listener.onFailure();
            }

            @Override
            public void onResponse(Call call, okhttp3.Response response) throws IOException {
                Event event = new Gson().fromJson(response.body().string(), Event.class);
                listener.onSuccess(event);
            }
        });
    }

    /**
     * Retrofit用のインターフェース：本当は別ファイルに定義した方がいいと思います
     */
    interface Api {
        @GET("get/event/")
        retrofit2.Call<Event> api();
    }

    /**
     * Retrofit2による非同期GET通信
     *
     * @param listener リスナー
     */
    private void getRetrofit(final Listener listener) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://xxxx.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        Api api = retrofit.create(Api.class);

        api.api().enqueue(new retrofit2.Callback<Event>() {
            @Override
            public void onResponse(retrofit2.Call<Event> call, retrofit2.Response<Event> response) {
                listener.onSuccess(response.body());
            }

            @Override
            public void onFailure(retrofit2.Call<Event> call, Throwable t) {
                listener.onFailure();
            }
        });
    }
}
```

見ての通り、retrofitだけ少し準備部分が独特ですが通信実行時からのコールバックの流れはVolleyとほとんど変わりがありません。処理を一つ移行するときは時間がかかるかもしれませんが、その後はスムーズに進められるんじゃないでしょうか。

## ちょっとだけ注意点（OkHttpとRetrofitの関係性について）

RetrofitはOkHttpをベースに作られたライブラリのため、OkHttpとRetrofitでOkHttpのバージョンが異なることがあります（ややこしい）。

だいたい半年程前はOkHttp自体はv3が公開されていたのに対し、Retrofit内のOkHttpはv2を利用していたことがありましたが、現在はv3を利用しています。

こうした「ズレ」もありますので、ライブラリの採択やアップデート対応には十分留意するようにしましょう。

[square/retrofit: Type-safe HTTP client for Android and Java by Square, Inc.](https://github.com/square/retrofit)

## 参考リンク

- [google/volley](https://github.com/google/volley)
- [square/okhttp: An HTTP+HTTP/2 client for Android and Java applications.](https://github.com/square/okhttp)
- [square/retrofit: Type-safe HTTP client for Android and Java by Square, Inc.](https://github.com/square/retrofit)