一、前言
每种计算机语言都有自己的过人之处,java适合Android开发、大数据处理,python适合做机器视觉、人工智能,但是如果我们想要完成一些强大的功能时,仅仅使用一种计算机语言肯定不能轻易满足我们的需求,如果可以将不同的语言混合使用,我们就可以利用各个语言的强项来实现我们所需的不同的功能。
移动互联网时代,手机已经已经成为我们必备的设备,如果能有一个功能强大的手机app将极大地便利我们的生活。但是网上并没有详细的教程实现在android中使用python接口,要么就是博客时间久远不适用现在,要么就是一篇水文浪费大家时间。下面我将在Android开发中调用python编写的API接口进行数据通信。
二、准备
本人的开发环境如下:
本项目的目录结构如下:
三、编写Python代码
创建一个Python项目,创建androidAPI.py和SSL.py,将下列代码粘贴进去:
- SSL.py:
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
import datetime
# 生成私钥
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
# 创建证书主题
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, u"example.com"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"My Organization"),
])
# 生成证书
cert = x509.CertificateBuilder().subject_name(
subject
).issuer_name(
issuer
).public_key(
private_key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=365)
).add_extension(
x509.SubjectAlternativeName([x509.DNSName(u"example.com")]),
critical=False,
).sign(private_key, hashes.SHA256(), default_backend())
# 保存私钥和证书
with open("private.key", "wb") as f:
f.write(private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()
))
with open("certificate.crt", "wb") as f:
f.write(cert.public_bytes(serialization.Encoding.PEM))
print("证书和私钥已生成。")
这段代码负责创建一个自签名的SSL证书,当执行完成之后,会在当下目录生成签名文件和秘钥文件。
- androidAPI.py:
from flask import Flask, request, jsonify
from flask_sslify import SSLify
import cv2 as cv
app = Flask(__name__)
sslify = SSLify(app)
# 示例数据
data = {
"message": "hello,my friend",
"status": "success"
}
message = "ni hao"
# 根路由
@app.route('/')
def home():
return "Welcome to the Python API"
# 示例接口
@app.route('/api/get_message', methods=['GET'])
def get_message():
image = cv.imread(r"C:\Users\18388\Desktop\7ae9758d7aa0c9d818f3567310b347e.jpg", 0)
gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
return gray_image
# 接受POST请求的接口
@app.route('/api/post_data', methods=['POST'])
def post_data():
received_data = request.json
return jsonify({"received_data": received_data, "status": "data received"})
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000, ssl_context=(r'.\certificate.crt', r'.\private.key'))
我们使用了flask接口,创建了两个对外的web接口,分别是get_message()和post_data()。运行后,会控制板上会出现我们搭建的web端口和ip地址。这两个都是本机地址,一样的。我们此次采用的是https协议,方便映射到外网。
四、编写android代码
-
添加网络请求权限:
我们创建一个android项目,在AndroidManifest.xml中添加网络请求权限代码。
<uses-permission android:name="android.permission.INTERNET" />
- 添加maven仓库和okhttp网络请求库:
android新版本中,需要在settings.gradle中添加jetpack组件库。
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
maven仓库配置好后,在build.gradle中的dependencies中添加
implementation 'com.squareup.okhttp3:okhttp:3.14.+'
- 编写Python.java:
新建一个EmptyActivity,贴入以下代码:
package com.example.modbustcp.python;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.example.modbustcp.R;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import java.io.IOException;
public class Python extends AppCompatActivity {
//忽略证书不安全错误
private OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(SSLSocketClient.getSSLSocketFactory())
.hostnameVerifier(SSLSocketClient.getHostnameVerifier())
.build();
Button button;
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_python2);
button = findViewById(R.id.button1);
textView = findViewById(R.id.textView1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 发送GET请求
sendGetRequest();
}
});
}
private void sendGetRequest() {
String url = "https://115.236.153.172:56631/api/get_message";
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("获取请求失败");
}
});
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
final String responseData = response.body().string();
runOnUiThread(new Runnable() {
@SuppressLint("SetTextI18n")
@Override
public void run() {
// 处理响应数据
Log.d("获取数据:", responseData);
textView.setText("数据为:" + responseData);
}
});
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("响应失败");
}
});
}
}
});
}
//发送post请求
private void sendPostRequest() {
String url = "http://115.236.153.172:24529/api/post_data";
MediaType JSON = MediaType.get("application/json; charset=utf-8");
// 示例数据
String json = "{\"name\": \"John\", \"age\": 30}";
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("获取失败");
textView.setText(e.getMessage());
System.out.println("获取失败的原因是"+e.getMessage());
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
final String responseData = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
// 处理响应数据
Log.d("POST Response", responseData);
textView.setText("数据为:" + responseData);
}
});
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("响应失败");
}
});
}
}
});
}
}
修改好端口和ip地址,便可以实现http通信了。