Retrofitでアレをやりたい時のTips

AndroidでAPIとやり取りするライブラリとして、Square社の[Retrofit](http://square.github.io/retrofit/)があります。
こんな感じでとても簡潔に処理が記述できます。

```java
MainApplication.API.getUsers(new Callback<List<User>>() {
    @Override
    public void failure(RetrofitError e) {
        //
    }

    @Override
    public void success(List<User> users, Response response) {
        //
    }
});
``` 

メリットをまとめるとこんな感じです。

* Callbackクラスのインターフェースメソッドがわかりやすくていい感じ。
* URLを各処理の中ではなく別のクラスにまとめて書いておくことができて見通しがいい。
* Gsonでのparseも初期化処理で記述しておけば各処理の中で書かなくていい。
* RestAdapterというクラスを使うことでDIしやすくなる。
* モック用のライブラリも梱包されておりテストも書きやすい。

便利なんですが、実際に使う場合にアレどうやったらいいの？ってことがあったのでまとめておこうと思います。


#1. Gsonなどでconvertする前に何か処理をしたい
初期化時に、`setConverter`で独自のCoverterクラスをセットします。ちょっと汚いですが。。

```java
public static final ApiService API = new RestAdapter.Builder()
            .setEndpoint("")
            .setConverter(new CustomGsonConverter())
            .build()
            .create(ApiService.class);
```

```java
public class CustomGsonConverter extends GsonConverter {

    private Gson gson;

    public CustomGsonConverter() {
        this(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create());
    }

    public ApiGsonConverter(Gson gson) {
        super(gson);
        this.gson = gson;
    }

    @Override
    public Object fromBody(TypedInput body, Type type) throws ConversionException {
        String charset = "UTF-8";
        if (body.mimeType() != null) {
            charset = MimeUtil.parseCharset(body.mimeType());
        }

        InputStreamReader isr = null;
        try {
            ByteArrayOutputStream baos = inputStreamToOutputStream(body.in());

            // ここで何かする

            isr = new InputStreamReader(new ByteArrayInputStream(baos.toByteArray()), charset);
            return gson.fromJson(isr, type);
        } catch (IOException e) {
            throw new ConversionException(e);
        } catch (JsonParseException e) {
            throw new ConversionException(e);
        } finally {
            if (isr != null) {
                try {
                    isr.close();
                } catch (IOException ignored) {
                }
            }
        }
    }
```


#2. 通信時にsession_store_keyを仕込みたい
初期化時に、`setRequestInterceptor `で独自のInterceptorクラスをセットします。

```java
public static final ApiService API = new RestAdapter.Builder()
            .setEndpoint("")
            .setRequestInterceptor(new CustomRequestInterceptor())
            .build()
            .create(ApiService.class);
```

```java
public class CustomRequestInterceptor implements RequestInterceptor {

    private static final String HEADER_COOKIE = "Cookie";
    private static final String EQ = "=";

    @Override
    public void intercept(RequestInterceptor.RequestFacade request) {
        request.addHeader(HEADER_COOKIE, Constants.SESSION_STORE_KEY + EQ + "sessionId");
    }
}
```

#3. 通信エラーが起きた時に共通処理をしたい
初期化時に、`setErrorHandler`で独自のErrorHandlerクラスをセットします。

```java
public static final ApiService API = new RestAdapter.Builder()
            .setEndpoint("")
            .setErrorHandler(new CustomErrorHandler())
            .build()
            .create(ApiService.class);
```

```java
public class CustomErrorHandler implements ErrorHandler {

    @Override
    public Throwable handleError(RetrofitError cause) {
        try {
            Response response = cause.getResponse();
            // ここで何かする
        } catch (Exception e) {
            //
        }
        return cause;
    }
}
```

#4. 通信時間を計測したい
初期化時に、`setProfiler`で独自のProfilerクラスをセットします。

```java
public static final ApiService API = new RestAdapter.Builder()
            .setEndpoint("")
            .setProfiler(new CustomProfiler())
            .build()
            .create(ApiService.class);
```

```java
public class CustomProfiler implements Profiler {
    @Override
    public Object beforeCall() {
        // afterCallに渡したいデータがあればここで返す
        return null;
    }

    @Override
    public void afterCall(RequestInformation requestInfo, long elapsedTime,
                          int statusCode, Object beforeCallData) {
        try {
            // ここで何かする。beforeCallDataには、beforeCall()で返されたObjectが入る
        } catch (Exception e) {
            //
        }
    }
}
```