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

  • 7
    いいね
  • 0
    コメント

はじめに

この記事は、DroidKaigi2017で発表した『いまからはじめるAndroid 6.0対応 〜Android 7.0から8.xを見つめて〜』の補完的な記事になります。

概要

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

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

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

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

実装

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

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

View-Client.png

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

サンプルコード

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

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

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

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.

参考リンク