4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【備忘録】http通信関連(okhttp3)

Last updated at Posted at 2020-11-03

#通信する
スマホやタブレットでアプリを動かす以上、避けて通れないのが通信。
今回はhttp通信についてまとめる。
流石に色々なライブラリがあるようだが、今回はOkhttp3を使った。

#gradleからインポート
まずはアプリでOkhttp3が使えるようにする。
そのためにbuild.gradle(モジュール:app)に追記する。
バージョンはお好きなものを。
追記したら忘れずに同期させよう。

build.gradle
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"
を合わせて追記する

AndroidManifests.xml
<?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で動いたというのが選定理由である。
とりあえず完成品を載せておく。

HttpConnector.java
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()は文字通り端末がネットワークに接続されているか確認するものだ。
当方ではこれを使ってアプリのオンライン/オフラインを切り替えるのに使用している。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?