#通信する
スマホやタブレットでアプリを動かす以上、避けて通れないのが通信。
今回はhttp通信についてまとめる。
流石に色々なライブラリがあるようだが、今回はOkhttp3を使った。
#gradleからインポート
まずはアプリでOkhttp3が使えるようにする。
そのためにbuild.gradle(モジュール:app)に追記する。
バージョンはお好きなものを。
追記したら忘れずに同期させよう。
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:2.0.2'
implementation 'com.squareup.okhttp3:okhttp:4.8.0' //←この1行を追記
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
#manifestsに追記
Androidはデフォルトではアプリの通信を許可していないようなので、AndroidManifests.xmlをいじくる。
今回はINTERNETのほかに、端末がネットワークにつながっているかのチェックに必要だったACCESS_NETWORK_STATEも追記する。
その状態でも通信が失敗するので、内にandroid:usesCleartextTraffic="true"
を合わせて追記する
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="sample.pack">
<!--この下2行を追記-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
#Okhttp3
最近のバージョンのAndroidでアプリが通信する場合、非同期処理にしないとエラーになるらしい。
正直初心者にはこの辺りはチンプンカンプンだったので、色々いじくっていたらたまたまOkhttpで動いたというのが選定理由である。
とりあえず完成品を載せておく。
package sample.pack;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
//WebAPIとの通信関連
public class HttpConnector {
//接続先のurl
private String urlStr = "";
//レスポンス用文字列
private String respStr = "";
//レスポンス用JSONArray
private JSONArray respJsonArr = null;
//待機処理用
private int Roop_Count;
private int Timeout_Sec;
private CountDownLatch countDownLatch;
//setterとgetter
public String geturlStr(){
return urlStr;
}
public void seturlStr(String urlStr){
this.urlStr = urlStr;
}
//setterとgetter(レスポンス用 非同期処理対応)
public String getrespStr() throws InterruptedException {
countDownLatch.await(Timeout_Sec, TimeUnit.SECONDS);
return respStr;
}
public void setrespStr(String respStr){
countDownLatch.countDown();
this.respStr = respStr;
}
public JSONArray getrespJsonArr() throws InterruptedException {
countDownLatch.await(Timeout_Sec, TimeUnit.SECONDS);
return respJsonArr;
}
public void setrespJsonArr(JSONArray respJsonArr){
countDownLatch.countDown();
this.respJsonArr = respJsonArr;
}
//コンストラクタ
HttpConnector(int Roop_Count, int Timeout_Sec){
this.Roop_Count = Roop_Count;
this.Timeout_Sec = Timeout_Sec;
this.countDownLatch = new CountDownLatch(Roop_Count);
}
//端末がネットワークに接続されているか確認
public boolean checkConnection(Context context){
ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkCapabilities info = cm.getNetworkCapabilities(cm.getActiveNetwork());
return info != null;
}
//GET実行
public void doGet(String urlStr) {
Request request = new Request.Builder()
.url(urlStr)
.get()
.build();
conectHttp(request);
}
//POST実行
public void doPost(String url, String postdata) {
RequestBody body = RequestBody.create(postdata, MediaType.get("application/json; charset=Shift_JIS"));
final Request request = new Request.Builder()
.url(url)
.post(body)
.build();
conectHttp(request);
}
//Requestを送りResponseを受け取る
private void conectHttp(Request request) {
OkHttpClient client = new OkHttpClient();
client.newCall(request).enqueue(new Callback() {
//通信に失敗した場合
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
setrespStr("");
setrespJsonArr(null);
Log.d("状態","Failure");
}
//通信に成功した場合
@Override
public void onResponse(Call call, Response response) {
try {
setrespStr(response.body().string());
} catch (IOException | NullPointerException e) {
e.printStackTrace();
Log.d("状態","Empty");
setrespStr("");
}
try {
setrespJsonArr(new JSONArray(respStr));
} catch (JSONException e) {
e.printStackTrace();
Log.d("状態","Empty");
setrespJsonArr(null);
}
}
});
}
}
正直全く納得のいかないコードなので不本意ではあるが、今の実力だとこれが限界だった…
使い方としてはnewしてurlを渡して結果をStringかJSONArrayをgetterで受け取る。
非同期処理なので、doGet,DoPostを呼んだあと、すぐにgetterを呼んでも値がセットされていないため、思った動きをしてくれない。
そこでCountDownLatchを使用してsetterで値がセットされたらgetterのawaitが抜けるようにした。
コンストラクタではループ回数とタイムアウト時間を設定するようにしているが、このあたりあまりスマートじゃない気がしている。
そもそもgetterを使用しているのはonResponseから値がreturnできなかったからだ。
共通化を諦めようかとも思ったが、使用頻度が高そうなのでこんな感じに。
因みにgetもpostも中身はほとんど共通にしてしまった。
checkConnection()は文字通り端末がネットワークに接続されているか確認するものだ。
当方ではこれを使ってアプリのオンライン/オフラインを切り替えるのに使用している。