【Android】Gson & Volleyの基本的な実装

Volleyの基本的な使い方。

**Gsonを併用した場合の実装について記載**

# 概要
非常に簡単な説明

* **Request**
Volleyにおけるネットワーク通信処理(要求)そのもの。
接続先URL、ヘッダ情報、BODYパラメータやリトライポリシー等の通信に関わる情報を実装する。
またレスポンスに対する最初の振る舞いも実装する。

* **RequestQueue**
渡されたRequestを順番に実行していくクラス。
シングルインスタンスで扱うべきとされている。

* **ImageLoader**
設定したURLを元にリクエストを生成し、レスポンスがあれば指定のImageViewへ自動的に
画像をセットしてくれる。
シングルインスタンスで扱うべきとされている。

* **Gson**
Google製Json解析ライブラリ。

# 実装方法

## Gsonを使ったJson解析

VolleyとGsonをよく一緒に使うので、Gsonの使い方を簡単に説明。
例えば以下の様なJsonデータがあると

```lang:Json
{
  "string_data":"あいうえお",

  "integer_data":10,

  "array_data":[
    {
      "data1":"item1",
      "data2":20
    },
    
    {
      "data1":"item2",
      "data2":21
    },
    
    {
      "data1":"item3",
      "data2":22
    }
  ]
}
```

以下の様なオブジェクトを用意してやって

```lang:HogeType
public class HogeType {

    public String string_data;
    public Integer integer_data;
    public ArrayList<HogeArrayType> array_data;

    public class HogeArrayType {
        public String data1;
        public Integer data2;
    }

}
```

以下の様にパースしてやると

```lang:パース処理
String json = /* 文字列化したjsonデータとか */
Gson gson = new Gson();
HogeType type = gson.fromJson(json, HogeType.class);
```

同じ型、名前のフィールドに勝手にデータを投入してくれる
※名前が一致しても型が異なるとExceptionを吐くので注意しよう


## Request

基本的な実装

```lang:HogeRequest
public class HogeRequest extends Request<HogeType> {
    private final Gson gson = new Gson();
    private final Class<HogeType> clazz;
    private final HogeRequestResponseListener listener;
    private HashMap<String, String> params = new HashMap<>();

    /**
     * 独自データ型のレスポンスリスナーを実装
     */
    public interface HogeRequestResponseListener {
        void onResponse(HogeType response);
    }

    /**
     * リクエストのインスタンス取得
     *
     * @param listener      レスポンスリスナー
     * @param errorListener エラーリスナー
     * @return リクエストのインスタンス
     */
    public static HogeRequest get(HogeRequestResponseListener listener,
                                        Response.ErrorListener errorListener) {
        String url = "URL";
        return new HogeRequest(url, listener, errorListener);
    }

    /**
     * コンストラクタ
     *
     * @param url           URL
     * @param listener      正常終了時のネットワークレスポンス
     * @param errorListener 異常終了時のネットワークレスポンス
     */
    public HogeRequest(String url, HogeRequestResponseListener listener,
                             Response.ErrorListener errorListener) {
        super(Method.POST, url, errorListener);
        // 正常時終了時に返却するクラス型セット
        this.clazz = HogeType.class;

        // レスポンスリスナーセット
        this.listener = listener;

        // パラメータセット
        params = new HashMap<>();
        params.put(param1, param2);

    }

    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return params;
    }

    @Override
    protected void deliverResponse(HogeType response) {
        // 成形したデータを返す
        // リスナーが存在すればレスポンスを返す
        if (this.listener != null) {
            this.listener.onResponse(response);
        } else {
            deliverError(new VolleyError(""));
        }
    }

    @Override
    public void deliverError(VolleyError error) {
        // エラーレスポンス
        super.deliverError(error);
    }

    @Override
    protected Response<HogeType> parseNetworkResponse(NetworkResponse response) {
        // データを成形する
        // 成功：deliverResponse
        // 失敗：deliverError
        try {
            if (response != null && response.data != null && response.headers != null) {
                String json = new String(
                        response.data,
                        HttpHeaderParser.parseCharset(response.headers));
                return Response.success(
                        gson.fromJson(json, clazz),
                        HttpHeaderParser.parseCacheHeaders(response));
            } else {
                return Response.error(new ParseError(new NullPointerException("")));
            }
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JsonSyntaxException e) {
            return Response.error(new ParseError(e));
        }
    }
}
```

## RequestQueue

基本的な実装

```lang:Application
public class App extends Application {

    private RequestQueue requestQueue;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public RequestQueue getRequestQueue() {
        if (requestQueue == null) {
            requestQueue = Volley.newRequestQueue(this);
        }
        return requestQueue;
    }

}
```

```lang:使用例
App.getRequestQueue().add(HogeRequest.get());
```
## ImageLoader

基本的な実装

```lang:Application
public class App extends Application {

    private ImageLoader imageLoader;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public ImageLoader getImageLoader() {
        if (imageLoader == null) {
            imageLoader = new ImageLoader(getRequestQueue(), new ImageLruCache());
        }
        return imageLoader;
    }

}
```

```lang:ImageLruCache
public class ImageLruCache implements ImageLoader.ImageCache {

    private final LruCache<String, Bitmap> mMemoryCache;

    public ImageLruCache() {
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;

        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount() / 1024;
            }

            @Override
            protected void entryRemoved(boolean evicted, String key, Bitmap oldVal, Bitmap newVal) {
                super.entryRemoved(evicted, key, oldVal, newVal);
            }

            @Override
            protected Bitmap create(String key) {
                return super.create(key);
            }
        };
    }

    @Override
    public Bitmap getBitmap(String url) {
        return mMemoryCache.get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        mMemoryCache.put(url, bitmap);
    }
}
```

```lang:使用例
    public void setImageLoader(Context context, ImageView imageView, String imageUrl) {
        // 画像
        if (imageView != null && imageUrl != null) {
            try {
                ImageLoader.ImageContainer imageContainer =
                        (ImageLoader.ImageContainer) imageView.getTag();
                
                if (imageContainer != null) {
                    imageContainer.cancelRequest();
                }
                
            } catch (ClassCastException ignore) {
            }
            
            ImageLoader.ImageListener listener = 
                    ImageLoader.getImageListener(imageView, R.drawable.hoge, R.drawable.fuga);
            imageView.setTag(App.get().getImageLoader().get(imageUrl, listener));
        }
    }
```

# Tips（2015/09/09追記）

## Gsonパースする時の「null」値について

```
{
    "int_val":null
}
```

こういうデータに対してオブジェクトを用意するとき

```
int int_val;
```

こうすると自動で「0」が入り

```
Integer int_val;
```

こうすると「null」が入る