OkHttpを採用した理由
以前に Apache のHTTPクライアントを使ったことがあったので使うつもりでいたのですが、どうやらAndroid6.0リリースの時点でサポートが切れたようです。今回はAndroidで使うつもりはなかったのですが、どうせなら今後も使いまわせるようにしておきたかったので、別のHTTPクライアントを探したところ、OkHttpに行き当たりました。
OkHttp を選んだ理由3点
- Apache製よりも記述が簡単で直感的。メソッドチェーンで書ける。
- コネクションプールが使える。
- 非同期、同期のどちらも使える。
特に下の2点は、大量にリクエストを飛ばす必要が出てきたときに重宝しそうですね。
基本的な使い方
今回、作成したものは下記の5パターン
- 同期的に Get リクエストを行う get()
- 同期的に Post リクエストを行う post()
- 非同期的に Get リクエストを行う getAsync()
- 非同期的に Post リクエストを行う postAsync()
- 同期的に Get リクエストを行ない、レスポンスを InputStreamで受け取る getAsInputStream()
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.function.Consumer;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* Rest API クライアント
*/
public class RestApiClient {
/** JSON用MediaType */
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
/** OkHttpClient */
private final OkHttpClient client = new OkHttpClient();
/**
* Getリクエスト(同期)
*
* @param url URL
* @return レスポンス文字列
* @throws IOException ネットワーク接続などの障害でリクエストが実行できなかった場合
*/
public String get(String url) throws IOException {
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
ResponseBody body = response.body();
return body.string();
}
/**
* Getリクエスト(非同期)
*
* @param url URL
* @param onSuccess リクエスト成功時処理
* @param onError リクエスト失敗時処理
*/
public void getAsync(String url, Consumer<String> onSuccess, Consumer<IOException> onError) {
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
onSuccess.accept(response.body().string());
}
@Override
public void onFailure(Call call, IOException e) {
call.cancel();
onError.accept(e);
}
});
}
/**
* POSTリクエスト(同期)
*
* @param url URL
* @param json JSON文字列
* @return レスポンス文字列
* @throws IOException ネットワーク接続などの障害でリクエストが実行できなかった場合
*/
public String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
/**
* POSTリクエスト(非同期)
*
* @param url URL
* @param json JSON文字列
* @return レスポンス文字列
*/
public void postAsync(String url, String json, Consumer<String> onSuccess, Consumer<IOException> onError) {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
onSuccess.accept(response.body().string());
}
@Override
public void onFailure(Call call, IOException e) {
call.cancel();
onError.accept(e);
}
});
}
/**
* Getリクエスト(同期。ファイルや画像などバイナリを返される場合に使用)
*
* @param url URL
* @return レスポンスをInputStreamで返す
* @throws IOException ネットワーク接続などの障害でリクエストが実行できなかった場合
*/
public InputStream getAsInputStream(String url) throws IOException {
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
return response.body().source().inputStream();
}
/**
* 動作確認
*/
public static void main(String[] args) throws IOException {
System.out.println("start");
RestApiClient2 app = new RestApiClient2();
// Get(同期)のテスト
String resGet = app.get("http://your-target-host/");
System.out.println(resGet);
// Post(同期)のテスト
String resPost = app.post("http://your-target-host/", "{\"key\":\"value\"}");
System.out.println(resPost);
// Get(非同期)のテスト
app.getAsync("http://your-target-host/", x -> System.out.println(x), e -> System.out.println(e.getMessage()));
// Post(非同期)のテスト
app.postAsync("http://your-target-host/", "{\"key\":\"value\"}", x -> System.out.println(x), e -> System.out.println(e.getMessage()));
// Get(同期。ファイルを返す場合)のテスト
File file = new File("./", "response.dat");
InputStream is = app.getAsInputStream("https://clover-creation.com/");
Files.copy(is, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
System.out.println("end");
}
}
注意したところは下記の3点
- OkHttpClientのインスタンスをインスタンス変数にもって、各メソッド間で使いまわしています。こうすることによって、OkHttpが内部に持っているコネクションプールが活用できます。
- 非同期処理では、リクエスト後の処理をConsumer<String>でもらうようにしています。こうすることによって、RestApiClientクラスを汎用的に使えるようにしています。Stringの代わりにResponseをそのまま渡しても良いのですが、RestApiClientクラスを使用するクラス側がOkHttpに依存してしまうので止めました。
- バイナリデータを受け取る場合には、StringではなくInputStreamで返します。あとはファイルに保存するなり、データベースに保存するなりできそうです。
次に準備すること
JavaでJSONを扱う方法について調べてみようと思います。
- 引数で渡しているJSON文字列を構築する方法
- 戻り値で返されたJSON文字列をパースする方法