AndroidでHTTP通信したいときの手段まとめ

**2019/04/25更新**
AndroidでHTTP通信するときの手段についてまとめておきます。

サーバー側の流行やプログラミングのパラダイム変化によって今後もどんどん新しい、便利なライブラリが出てくるでしょう。

# HttpUrlConnection(ライブラリ使わずJava標準のURLConnectionを使う方法)
https://developer.android.com/reference/java/net/HttpURLConnection.html

- Apache系(Apache HttpClient,DefaultHttpClient,AndroidHttpClient)引退にともない、低レベルAPIとしてはこちらが推奨に。
(以前はバグがあった)

## Example

■ **HttpUrlConnectionでGET**のサンプルコード

```java:GET
    public static String get(String endpoint, String encoding, Map<String, String> headers) throws IOException {

        final int TIMEOUT_MILLIS = 0;// タイムアウトミリ秒：0は無限

        final StringBuffer sb = new StringBuffer("");

        HttpURLConnection httpConn = null;
        BufferedReader br = null;
        InputStream is = null;
        InputStreamReader isr = null;

        try {
            URL url = new URL(endpoint);
            httpConn = (HttpURLConnection) url.openConnection();
            httpConn.setConnectTimeout(TIMEOUT_MILLIS);// 接続にかかる時間
            httpConn.setReadTimeout(TIMEOUT_MILLIS);// データの読み込みにかかる時間
            httpConn.setRequestMethod("GET");// HTTPメソッド
            httpConn.setUseCaches(false);// キャッシュ利用
            httpConn.setDoOutput(false);// リクエストのボディの送信を許可(GETのときはfalse,POSTのときはtrueにする)
            httpConn.setDoInput(true);// レスポンスのボディの受信を許可

            // HTTPヘッダをセット
            if (headers != null) {
                for (String key : headers.keySet()) {
                    httpConn.setRequestProperty(key, headers.get(key));// HTTPヘッダをセット
                }
            }
            httpConn.connect();

            final int responseCode = httpConn.getResponseCode();

            if (responseCode == HttpURLConnection.HTTP_OK) {

                is = httpConn.getInputStream();
                isr = new InputStreamReader(is, encoding);
                br = new BufferedReader(isr);
                String line = null;
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
            } else {
                // If responseCode is not HTTP_OK
            }

        } catch (IOException e) {
            throw e;
        } finally {
            // fortify safeかつJava1.6 compliantなclose処理
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                }
            }
            if (isr != null) {
                try {
                    isr.close();
                } catch (IOException e) {
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                }
            }
            if (httpConn != null) {
                httpConn.disconnect();
            }
        }
        return sb.toString();
    }
```

```java:GET呼び出し方
        //HTTPヘッダ(指定したければ)
        Map<String,String> headers=new HashMap<String,String>();
        headers.put("X-Example-Header","Example-Value");
        String resultStr = get("http://hogehoge/foofoo", "UTF-8", headers);
```

■ **HttpUrlConnectionでPOST**(JSON文字列をPOSTする例)

```java:POST
   public static String post(String endpoint, String encoding, Map<String, String> headers, String jsonString) throws IOException {

        final int TIMEOUT_MILLIS = 0;// タイムアウトミリ秒：0は無限

        final StringBuffer sb = new StringBuffer("");

        HttpURLConnection httpConn = null;
        BufferedReader br = null;
        InputStream is = null;
        InputStreamReader isr = null;

        try {
            URL url = new URL(endpoint);
            httpConn = (HttpURLConnection) url.openConnection();
            httpConn.setConnectTimeout(TIMEOUT_MILLIS);// 接続にかかる時間
            httpConn.setReadTimeout(TIMEOUT_MILLIS);// データの読み込みにかかる時間
            httpConn.setRequestMethod("POST");// HTTPメソッド
            httpConn.setUseCaches(false);// キャッシュ利用
            httpConn.setDoOutput(true);// リクエストのボディの送信を許可(GETのときはfalse,POSTのときはtrueにする)
            httpConn.setDoInput(true);// レスポンスのボディの受信を許可

            if (headers != null) {
                for (String key : headers.keySet()) {
                    httpConn.setRequestProperty(key, headers.get(key));// HTTPヘッダをセット
                }
            }

            OutputStream os = httpConn.getOutputStream();
            final boolean autoFlash = true;
            PrintStream ps = new PrintStream(os, autoFlash, encoding);
            ps.print(jsonString);
            ps.close();

            final int responseCode = httpConn.getResponseCode();

            if (responseCode == HttpURLConnection.HTTP_OK) {

                is = httpConn.getInputStream();
                isr = new InputStreamReader(is, encoding);
                br = new BufferedReader(isr);
                String line = null;
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
            } else {
                // If responseCode is not HTTP_OK
            }

        } catch (IOException e) {
            throw e;
        } finally {
            // fortify safeかつJava1.6 compliantなclose処理
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                }
            }
            if (isr != null) {
                try {
                    isr.close();
                } catch (IOException e) {
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                }
            }
            if (httpConn != null) {
                httpConn.disconnect();
            }
        }
        return sb.toString();
    }
```

```java:POST呼び出し方
        String postJson = "[{\"message\":{\"number\":\"1\",\"value\":\"Hello\"}}]";
        Map<String, String> headers = new HashMap<String, String>(); // HTTPヘッダ(指定したければ)
        headers.put("X-Example-Header", "Example-Value");
        String postResult = post("http://hogehoge/foofoo", "UTF-8", headers, postJson);
```

　脱線しますが、Android盤のjava.net.HttpURLConnectionは、本家JDKと同じインタフェースはもっていますが、AndroidのJava実装は本家OracleJDKとは違います（※１）ので、Oracle謹製のJavaランタイムで動作していても、Androidで動作しないことは多々ありました。（※２）

（※１）AndroidのJDKはApache HarmonyベースからOpenJDKベースへと変化しています。
（※２）Pure Java（のみ）でテストされたライブラリをそのままもってくると痛い目をみます。



# Apache系(Apache HttpClient,DefaultHttpClient,AndroidHttpClient)
https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html?hl=ja#behavior-apache-http-client

- 5.1でDeprecatedになり、6.0で削除されました
- 経過措置はあるものの、もう使用しないほうがいいでしょう

# volley
https://developer.android.com/training/volley/index.html

- Androidでよくあるシーン（非同期とか）に対応したいろいろ便利なvolley



```java
final TextView mTextView = (TextView) findViewById(R.id.text);
...

// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.google.com";

// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        // Display the first 500 characters of the response string.
        mTextView.setText("Response is: "+ response.substring(0,500));
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        mTextView.setText("That didn't work!");
    }
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
```
https://developer.android.com/training/volley/simple.html より抜粋

# OkHttp3
 http://square.github.io/okhttp/ 
- 最近人気がある（らしい）
- 現時点で最新は　OkHttp 3系

## maven/gradle
https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp

```gradle設定例
dependencies {
    compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.14.1'
}
```

## Example

OkHttp3でのREST操作(Request)のつくりかた

**GET**

```java
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

   public void doGet(String url) throws IOException {

        final Request request = new Request.Builder().url(url).build();
        final OkHttpClient client = new OkHttpClient.Builder().build();

        //同期呼び出し
        Response response = client.newCall(request).execute();
        //結果を表示
        System.out.println(response.body().string());
    }
```

**POST**

- Form POSTしたい場合

```java
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

   public void doFormPost(String url) throws IOException {
        final FormBody.Builder formBodyBuilder = new FormBody.Builder();
        formBodyBuilder.add("param_name", "param_value");

        final Request request = new Request.Builder()
                .url(url)
                .header("User-Agent", "Example client")
                .post(formBodyBuilder.build())
                .build();
        OkHttpClient client = new OkHttpClient.Builder()
                .build();
        //同期呼び出し
        Response response = client.newCall(request).execute();
        //結果を表示
        System.out.println(response.body().string());
    }
```

  
  

- JSON をまるっとPOSTしたい場合
 
```java
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

    public void doPost(String url, String jsonString) throws IOException {
        okhttp3.MediaType mediaTypeJson = okhttp3.MediaType.parse("application/json; charset=utf-8");

        RequestBody requestBody = RequestBody.create(mediaTypeJson, jsonString);

        final Request request = new Request.Builder()
                .url(url)
                .post(requestBody)//POST指定
                .build();
        OkHttpClient client = new OkHttpClient.Builder()
                .build();
        //同期呼び出し
        Response response = client.newCall(request).execute();
        //結果を表示
        System.out.println(response.body().string());
    }

```

**PUT**

```java
final Builder formBodyBuilder = new FormBody.Builder();
	formBodyBuilder.add("param_name","param_value");
	
	final Request request = new Request.Builder()
			.url(url)
			.put(formBodyBuilder.build())
			.build();
	OkHttpClient client = new OkHttpClient.Builder()
                .build();
	Response response = client.newCall(request).execute();//同期呼び出し
```

**DELETE**

```java
	final Request request = new Request.Builder()
			.url(url)
			.delete()
			.build();
	OkHttpClient client = new OkHttpClient.Builder()
                .build();
	Response response = client.newCall(request).execute();//同期呼び出し
```

**同期呼び出し**

```java
OkHttpClient client = new OkHttpClient.Builder()
				.build();
Response response = client.newCall(request).execute();
```

**非同期呼び出し**

```java
OkHttpClient client = new OkHttpClient.Builder()
				.build();

client.newCall(request).enqueue(new okhttp3.Callback() {

@Override
public void onResponse(Call call, Response response) throws IOException {

final String resString = response.body().string();
 //view更新のときは handler#post()する
}

@Override
public void onFailure(Call call, IOException arg1) {

}
});
```


# Retrofit2
http://square.github.io/retrofit/
- OkHttp開発元による「RESTならまかせてよ！」というライブラリ。
- REST操作(GET,POST,PUT,DELETE)を少ない記述で実現できる
- 現時点で最新は Retrofit 2.x系

### maven/gralde
https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit

### Example
```
@Multipart
@PUT("user/photo")
Call<User> updateUser(@Part("photo") RequestBody photo, @Part("description") RequestBody description);
```
( http://square.github.io/retrofit/ より抜粋)

# HTTP通信と一緒に使う道具たち



## JSONパーサー

JSONをモデルクラス(POJO)にマッピングしたい

## JSONパーサーを使うときに、GSONやJacksonに対応したモデルクラスを自動生成してくれるサイト

生のJSONやJSON Schemaから、GSONやJacksonに対応したモデルクラスを自動生成してくれる

http://www.jsonschema2pojo.org/


### ■ **GSON**
https://mvnrepository.com/artifact/com.google.code.gson/gson

**maven/gradle**
https://mvnrepository.com/artifact/com.google.code.gson/gson

**設定例**

```groovy:build.gradle
 compile 'com.google.code.gson:gson:2.8.5'
```

### ■ **Jackson**
https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind

**（おまけ）JacksonをJSON ParserとしてAndroidで使う時の注意**

```compile 'com.fasterxml.jackson.core:jackson-databind:2.8.6'```

gradleのビルド時にJACKSONのLICENSEファイルが重複して以下のようなエラーメッセージがでるので、

```
Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'.
> com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK META-INF/LICENSE
```

アプリレベルのbuild.gradleに以下のように書いておくとよいでしょう。

```groovy:build.gradle
android {
 中略
 packagingOptions {

        exclude 'META-INF/LICENSE'

    }
}
```

JSONの操作については、↓に詳しくまとめました
[JSONをJavaに、JavaをJSONにする　～GSONとJacksonの使用方法～](https://qiita.com/riversun/items/cdd990e96c2bbf9cb043)



### リアクティブプログラミング
**RxJava/RxAndroid**
https://mvnrepository.com/artifact/io.reactivex/rxandroid
https://mvnrepository.com/artifact/io.reactivex/rxjava