G2EX

Android App与Flask通信实例:POST数据的发送与处理

一、说明

在Android开发中,当应用需要向服务器提交数据数据时,可以使用POST方式向服务器提交form表单。服务器端框架有多种选择,快速开发可以使用Python的Flask框架。本文介绍了在服务器端如何使用Flask框架处理客户端发送的POST请求,以及在Android App中如何向服务器发送POST请求。

实验环境
服务器:Ubuntu 14.04 x64, IP地址: 10.10.10.128
Android IDE:Android Studio 1.4.1
辅助测试工具:Chrome应用Postman

本文中,Android客户端使用POST的方式向服务器提交form表单。我们假设需要提交的form表单只有两个字段,分别是uid="001"name="g2ex"

接下来,我们先编写服务器端Flask应用,接收客户端发送的POST数据,解析出这两个参数的内容,并向客户端返回确认信息OK.。然后再在Android Studio中编写客户端App实现POST提交数据的过程。

二、服务器端开发

本文中,服务器端使用Python的Flask框架。想要快速了解Flask,可以参考Flask中文开发文档。Flask的安装请参考Flask安装说明

1) 编写一个简单的Flask应用

使用Flask处理客户端的POST请求非常简单,我们创建一个项目目录flaskr,在目录下创建一个名为myapp.py的文件,写入如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask, request

# create the application object
app = Flask(__name__)

@app.route('/', methods=['POST'])
def post():
uid = request.form['uid']
name = request.form['name']
print 'uid: %s, name: %s' % (uid, name)
return 'OK.'

if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True)

使用下面的命令启动Flask,默认在本机的5000端口上监听来自所有网络的POST请求。

1
python myapp.py

2) 简单测试Flask

在Chrome浏览器chrome://apps中打开Postman应用。类型选择POST,服务器地址中输入http://10.10.10.128:5000Body中勾选x-www-form-urlencoded,并在下面的Key-Value中输入我们要提交的内容。点击Send可以看到服务器返回的OK.,说明我们编写的Flask应用可以处理POST请求。如下图所示。

Postman POST测试

此时,在服务器端的Terminal中,可以看到打印出来的记录:

1
2
uid: 001, name: g2ex
10.10.10.1 - - [18/Nov/2015 22:52:25] "POST / HTTP/1.1" 200 -

三、Android客户端开发

本文中,Android客户端使用POST的方式向服务器提交form数据,可以编写HttpUtils类实现这个功能,HttpUtils类中使用了标准Java接口HttpURLConnection

首先,使用Android Studio创建一个名为PostTest的项目,在布局文件activity_main.xml中创建一个id为button_send的按钮。

MainActivity中的onCreate()中添加按钮监听事件,按钮点击后调用postData()postData()启动线程使用HttpUtils类的静态方法submitPostData()向服务器POST数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/* MainActivity.java */

private Button mButton_send;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mButton_send = (Button)findViewById(R.id.button_send);
mButton_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
postData();
}
});
}

private void postData() {
new Thread(new Runnable() {
@Override
public void run() {
Map<String, String> params = new HashMap<String, String>();
String post_result = null;
params.put("uid", "001");
params.put("name", "g2ex");
try {
post_result = HttpUtils.submitPostData(params, "utf-8");
Log.i("POST_RESULT", post_result);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}).start();
}

HttpUtils类有三个静态方法:

  • submitPostData()方法发送POST请求到服务器并返回服务器信息
  • getRequestData()方法用于封装请求体
  • dealResponseResult()方法处理服务器的响应结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/* HttpUtils.java */

public class HttpUtils {
public static String submitPostData(Map<String, String> params, String encode) throws MalformedURLException {
/**
* 发送POST请求到服务器并返回服务器信息
* @param params 请求体内容
* @param encode 编码格式
* @return 服务器返回信息
*/
byte[] data = getRequestData(params, encode).toString().getBytes();
URL url = new URL("http://10.10.10.128:5000/");
HttpURLConnection httpURLConnection = null;
try{
httpURLConnection = (HttpURLConnection)url.openConnection();
httpURLConnection.setConnectTimeout(3000); // 设置连接超时时间
httpURLConnection.setDoInput(true); // 打开输入流,以便从服务器获取数据
httpURLConnection.setDoOutput(true); // 打开输出流,以便向服务器提交数据
httpURLConnection.setRequestMethod("POST"); // 设置以POST方式提交数据
httpURLConnection.setUseCaches(false); // 使用POST方式不能使用缓存
// 设置请求体的类型是文本类型
httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// 设置请求体的长度
httpURLConnection.setRequestProperty("Content-Length", String.valueOf(data.length));
// 获得输入流,向服务器写入数据
OutputStream outputStream = new BufferedOutputStream(httpURLConnection.getOutputStream());
outputStream.write(data);
outputStream.flush(); // 重要!flush()之后才会写入

int response = httpURLConnection.getResponseCode(); // 获得服务器响应码
if (response == HttpURLConnection.HTTP_OK) {
InputStream inputStream = httpURLConnection.getInputStream();
return dealResponseResult(inputStream); // 处理服务器响应结果
}
} catch (IOException e) {
e.printStackTrace();
} finally {
httpURLConnection.disconnect();
}

return "";
}

/**
* 封装请求体信息
* @param params 请求体内容
* @param encode 编码格式
* @return 请求体信息
*/
public static StringBuffer getRequestData(Map<String, String> params, String encode) {
StringBuffer stringBuffer = new StringBuffer(); //存储封装好的请求体信息
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
stringBuffer.append(entry.getKey())
.append("=")
.append(URLEncoder.encode(entry.getValue(), encode))
.append("&");
}
stringBuffer.deleteCharAt(stringBuffer.length() - 1); // 删除最后一个"&"
} catch (Exception e) {
e.printStackTrace();
}
return stringBuffer;
}

/**
* 处理服务器的响应结果(将输入流转换成字符串)
* @param inputStream 服务器的响应输入流
* @return 服务器响应结果字符串
*/
public static String dealResponseResult(InputStream inputStream) {
String resultData = null;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] data = new byte[1024];
int len = 0;
try {
while ((len = inputStream.read(data)) != -1) {
byteArrayOutputStream.write(data, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
resultData = new String(byteArrayOutputStream.toByteArray());
return resultData;
}
}

最后,记得在AndroidManifest.xml中加入网络权限:

1
<uses-permission android:name="android.permission.INTERNET"/>

运行应用,在Android Studio logcat中可以看到输出的日志I/POST_RESULT: OK.,服务器终端中也可以看到打印的内容和访问记录。

源码下载:http://pan.baidu.com/s/13G7LW 密码: 4ww5

四、参考内容

  1. http://docs.jinkan.org/docs/flask/ 《Flask中文开发文档》
  2. http://www.cnblogs.com/menlsh/archive/2013/05/22/3091983.html 《Android学习笔记46:使用Post方式提交数据》。本文中的HttpUtils引用自该文章,文章中服务器端使用的是Apache Tomcat。