AndroidはHttp通信、JSON処理もいろいろオプションがあるのでたまに使う人間にはつらい。。。
最近はOkHttpが流行ってるらしいので使ってみる。
前提
- Http通信にはOkHttpを利用する
- JsonのパースはJSONObjectで処理(簡単な処理しかしない+追加ライブラリいらないので)
- 言語はJavaを利用する(大人の事情)
- 開発環境はAndroid Studio 3.4.x on Mac
仕様
とりあえずボタンを押したらWebAPIをキックしてJson取得して、パースして、表示したい。
サーバ上に下記のJsonを返すプログラムを設置。
{"status":"OK","message":"Hello2019-05-31 07:06:23"}
- ボタンクリックでJsonリクエスト
- 取得したJsonからstatuを取り出し、上記画面に表示。
下記のようなイメージ。
準備
実装前にいくつか準備。
INTERNETアクセスの許可
AndroidManifest.xmlに以下を追加。当たり前だが、当たり前過ぎて忘れている場合がある。
<uses-permission android:name="android.permission.INTERNET" />
通信ドメインの許可(非https)
どうやらAndroid9.xからhttps以外の通信はエラーとなるよう。下記のようなエラーが出る。
java.io.IOException: Cleartext HTTP traffic to hoge.com not permitted
hoge.comはアクセスしたいドメイン、サブドメイン。
許可するためには設定ファイルが必要。めんどうだが対応する。
設定ファイルの設置
下記の内容を記したxmlファイルを容易する。
別にファイル名や場所はどこでもいいみたい(次の過程で明示的に指定するので)。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">www.bluecode.jp</domain>
</domain-config>
</network-security-config>
設定ファイルの指定
指定した設定ファイルをAndroidManifest.xmlで指定します。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.bluecode.http01">
<application
+ android:networkSecurityConfig="@xml/network_security_config"
android:allowBackup="true"
...
OkHttpの設定
GrandleのModule:appの方に追加してSync。
...
dependencies {
...
implementation 'com.squareup.okhttp3:okhttp:4.0.0-alpha02'
}
...
準備は以上。
実装
いよいよ実装。もうずいぶん前から通信処理等はメインスレッドではできない。
別スレッドで起動し、かつ、非同期処理するというのが常識。OkHttpでは下記のように記述するみたい。
MainActivity.java
package jp.bluecode.http01;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
//Widget宣言
TextView txt01;
Button btn01;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Widget初期化
txt01 = findViewById(R.id.txt01);
btn01 = findViewById(R.id.btn01);
//ボタンクリック
btn01.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//httpリクエスト
try{
//okhttpを利用するカスタム関数(下記)
httpRequest("http://www.bluecode.jp/test/api.php");
}catch(Exception e){
Log.e("Hoge",e.getMessage());
}
}
});
}
void httpRequest(String url) throws IOException{
//OkHttpClinet生成
OkHttpClient client = new OkHttpClient();
//request生成
Request request = new Request.Builder()
.url(url)
.build();
//非同期リクエスト
client.newCall(request)
.enqueue(new Callback() {
//エラーのとき
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
Log.e("Hoge",e.getMessage());
}
//正常のとき
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
//response取り出し
final String jsonStr = response.body().string();
Log.d("Hoge","jsonStr=" + jsonStr);
//JSON処理
try{
//jsonパース
JSONObject json = new JSONObject(jsonStr);
final String status = json.getString("status");
//親スレッドUI更新
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(new Runnable() {
@Override
public void run() {
txt01.setText(status);
}
});
}catch(Exception e){
Log.e("Hoge",e.getMessage());
}
}
});
}
}
activity_main.xml
レイアウトはお好みでどう実装してもいい気がしますが、とりあえず参考まで。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/txt01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
android:text="Hello World!"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txt01" />
</android.support.constraint.ConstraintLayout>