はじめに
前回の記事では,httpbinをWebAPIとして使用し,HttpURLConnectionの使い方について解説しました.今回は,FlaskをWebAPIとして使用し,AndroidとFlask間でHTTP通信(GET/POST)を行ってみます.HttpURLConnectionやcurlコマンドの詳しい解説は,前回の記事を参考にしてください.
前提
*Android Studio 4.0.1
*targetSdkVersion 28
*Google Nexus 5x
curlコマンド, Python
*Ubuntu 20.04 LTS (WSL 2)
*Python 3.7.3
GETメソッド
GETメソッドを使ったHTTP通信は簡単に実現できます.Android側は前回の記事を参考にしてください.最後に示すサンプルコードにはAndroid側のコードも記述します.ここではFlask側に関して記述します.
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/get', methods=['GET'])
def get():
# GETリクエストに対するレスポンスを作成
response = {'result': 'success', 'status': 200}
# JSONオブジェクトとして返す
return jsonify(response)
curlコマンドを使ってリクエストを送ってみます.
curl http://127.0.0.1:5000/api/get
以下のようなレスポンスが出力されるはずです.
{
"result": "success",
"status": 200
}
POSTメソッド
POSTメソッドを使ったHTTP通信について説明します.
flaskでJSON形式のデータを読み込む
JSON形式のデータを読み込む方法としては3つあります.今回は,POSTリクエストで受け取ったデータを読み込むことになります.
from flask import Flask, request, jsonify
import json
app = Flask(__name__)
@app.route('/api/post', methods=['POST'])
def post():
# 方法1
# POSTで受け取ったデータをrequest.dataを取り出すとbytes型であることがわかる
print(type(request.data))
# 出力してもbytes型であることがわかる
print(request.data)
# bytes型なので文字列に直すためにdecodeする
print(request.data.decode('utf-8'))
# JSON形式で書かれている文字列をloadsメソッドによってディクショナリ型に変換できる
data = json.loads(request.data.decode('utf-8'))
print(data)
# 方法2
# loadsメソッドでは,文字列以外にbytes型,bytearray型を入れることができる
data = json.loads(request.data)
print(data)
# 方法3
# request.jsonメソッドを使うことで,POSTで受け取ったデータをディクショナリ型として扱うことができる
# この書き方が1番早いので推奨する
print(request.json)
# 方法3を採用し,戻り値としてJSONオブジェクトを返す
data = request.json
return jsonify(data)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
app.pyを実行して,試しにcurlを使ってPOSTリクエストを送ってみる.新しいターミナルを開き,以下のコマンドを実行する.bodyは,JSON形式で文字列を記述する.
curl -X POST -H 'Content-Type:application/json' -d `{"name": "foge", "value": "fogefoge"}` http://127.0.0.1:5000/api/post
curlコマンドを実行すると,app.pyを実行したターミナルに以下のように出力される.下3行の出力が同じであることが確認できる.
<class 'bytes'>
b'{"name": "foge", "value": "fogefoge"}'
{"name": "foge", "value": "fogefoge"}
{'name': 'foge', 'value': 'fogefoge'}
{'name': 'foge', 'value': 'fogefoge'}
{'name': 'foge', 'value': 'fogefoge'}
AndroidでのJsonのフォーマット
Androidから送信したJSON形式で書かれた文字列のデータをPythonで読み込むには,Androidの方でJSONの形式に則ってボディを記述する必要がある.具体的には以下のようにする.
String postData = "{\"name\": \"foge\", \"value\": \"fogefoge\"}";
このように,文字列の中に二重引用符を記述する必要があるためエスケープシーケンスを使って記述する.また,文字列の中に変数のデータを埋め込みたい場合は以下のように記述する.
String name = "foge";
int value = 100;
String postData = String.format("{\"name\": \"%s\", \"value\": %d}", name, value);
しかし,このように記述するのは非常に面倒なため,一般的には以下のように記述する.
// 連想配列を作成する
HashMap<String, Object> jsonMap = new HashMap<>();
jsonMap.put("name", "foge");
jsonMap.put("value", 100);
// 連想配列をJSONObjectに変換
JSONObject responseJsonObject = new JSONObject(jsonMap);
// JSONObjectを文字列に変換
String postData = responseJsonObject.toString();
サンプルコード
では,以下にサンプルコードを示す.
from flask import Flask, request, jsonify
app = Flask(__name__)
# jsonifyの結果に日本語が含まれる場合,以下の1行を記述することで文字化けを回避できる
app.config['JSON_AS_ASCII'] = False
@app.route('/api/get', methods=['GET'])
def get():
response = {"result": "success", "status": 200}
return jsonify(response)
@app.route('/api/post', methods=['POST'])
def post():
data = request.json
name = data['name']
value = data['value']
array = data['array']
print(f'data: {data}')
print(f'data["name"]: {name}')
print(f'data["value"]: {value}')
print(f'data["array"]: {array}')
print(f'data["array[0]"]: {array[0]}')
print(f'data["array[1]"]: {array[1]}')
print(f'data["array[2]"]: {array[2]}')
return jsonify(data)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
Androidのサンプルコードを記述する前にcurlで一度HTTP通信ができるかどうか確認を行ってください.
curl http://127.0.0.1:5000/api/get
curl -X POST -H 'Content-Type:application/json' -d '{"name": "foge", "value": 100, "array":["おはよう", "こんにちは", "こんばんは"]}' http://127.0.0.1:5000/api/post
以下にAndroidのサンプルコードを示します.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.samplehttpconnection">
<uses-permission android:name="android.permission.INTERNET"/>
<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">
<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>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:gravity="center"
android:text=""
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button2" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:text="GET"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:gravity="center"
android:text=""
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="POST"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.samplehttpconnection;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler();
private Button button;
private Button button2;
private TextView textView;
private TextView textView2;
// IPアドレスは各自変更してください.Pythonのプログラムを実行しているPCのIPアドレスを記述
private String urlGetText = "http://192.168.0.10:5000/api/get";
private String urlPostText = "http://192.168.0.10:5000/api/post";
private String getResultText = "";
private String postResultText = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button2 = findViewById(R.id.button2);
textView = findViewById(R.id.textView);
textView2 = findViewById(R.id.textView2);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String response = "";
try {
response = getAPI();
JSONObject rootJSON = new JSONObject(response);
getResultText = rootJSON.toString();
} catch (JSONException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
@Override
public void run() {
textView.setText(getResultText);
}
});
}
});
thread.start();
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String response = "";
try {
response = postAPI();
JSONObject rootJSON = new JSONObject(response);
postResultText = rootJSON.toString();
} catch (JSONException e) {
e.printStackTrace();
}
handler.post(new Runnable() {
@Override
public void run() {
textView2.setText(postResultText);
}
});
}
});
thread.start();
}
});
}
public String getAPI(){
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
String result = "";
String str = "";
try {
URL url = new URL(urlGetText);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(10000);
urlConnection.setReadTimeout(10000);
urlConnection.addRequestProperty("User-Agent", "Android");
urlConnection.addRequestProperty("Accept-Language", Locale.getDefault().toString());
urlConnection.setRequestMethod("GET");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(false);
urlConnection.connect();
int statusCode = urlConnection.getResponseCode();
if (statusCode == 200){
inputStream = urlConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
result = bufferedReader.readLine();
while (result != null){
str += result;
result = bufferedReader.readLine();
}
bufferedReader.close();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return str;
}
public String postAPI(){
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
OutputStream outputStream = null;
String result = "";
String str = "";
try {
URL url = new URL(urlPostText);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setConnectTimeout(10000);
urlConnection.setReadTimeout(10000);
urlConnection.addRequestProperty("User-Agent", "Android");
urlConnection.addRequestProperty("Accept-Language", Locale.getDefault().toString());
urlConnection.addRequestProperty("Content-Type", "application/json; charset=UTF-8");
urlConnection.setRequestMethod("POST");
urlConnection.setDoInput(true);
urlConnection.setDoOutput(true);
urlConnection.connect();
outputStream = urlConnection.getOutputStream();
HashMap<String, Object> jsonMap = new HashMap<>();
jsonMap.put("name", "foge");
jsonMap.put("value", 100);
ArrayList<String> array = new ArrayList<>();
array.add("おはよう");
array.add("こんにちは");
array.add("こんばんは");
jsonMap.put("array", array);
JSONObject responseJsonObject = new JSONObject(jsonMap);
String jsonText = responseJsonObject.toString();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, "utf-8"));
bufferedWriter.write(jsonText);
bufferedWriter.flush();
bufferedWriter.close();
int statusCode = urlConnection.getResponseCode();
if (statusCode == 200){
inputStream = urlConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
result = bufferedReader.readLine();
while (result != null){
str += result;
result = bufferedReader.readLine();
}
bufferedReader.close();
}
urlConnection.disconnect();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return str;
}
}