您的位置:首页 > 理论基础 > 计算机网络

Android HTTP协议请求网络(三)之HttpURLConnection方式

2017-05-08 16:49 323 查看
Android HTTP协议请求网络(一)之认识探索

Android HTTP协议请求网络(二)之HttpClient方式

githup练习demo地址:https://github.com/aiyangtianci/NetworkRequestDemo

一、介绍

在Android API Level 9(Android 2.2)之前只能使用DefaultHttpClient类发送http请求。DefaultHttpClient是Apache用于发送http请求的客户端,其提供了强大的API支持,而且基本没有什么bug,但是由于其太过复杂,Android团队在保持向后兼容的情况下,很难对DefaultHttpClient进行增强。为此,Android团队从Android API Level 9开始自己实现了一个发送http请求的客户端类——–HttpURLConnection。相比于DefaultHttpClient,HttpURLConnection比较轻量级,虽然功能没有DefaultHttpClient那么强大,但是能够满足大部分的需求,所以Android推荐使用HttpURLConnection代替DefaultHttpClient,并不强制使用HttpURLConnection。但从Android API Level 23(Android 6.0)开始,不能再在Android中使用DefaultHttpClient,强制使用HttpURLConnection。

二、Get方式:

实现流程步骤:
    第一步:实例化URL对象。    第二步:实例化HttpUrlConnection对象。    第三步:设置请求连接属性,传递参数等。    第四步:获取返回码判断是否链接成功。    第五步:读取输入流。    第六步:关闭链接。
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL("http://192.168.23.1:8080/TestProject/GetTest");
connection = (HttpURLConnection) url.openConnection();。
// 设置请求方法,默认是GET
connection.setRequestMethod("GET");
// 设置字符集
connection.setRequestProperty("Charset", "UTF-8");
// 设置文件类型
connection.setRequestProperty("Content-Type", "text/xml; charset=UTF-8");
// 设置请求参数,可通过Servlet的getHeader()获取
connection.setRequestProperty("Cookie", "AppName=" + URLEncoder.encode("你好", "UTF-8"));
// 设置自定义参数
connection.setRequestProperty("MyProperty", "this is me!");

if(connection.getResponseCode() == 200){
InputStream is = connection.getInputStream();
result = StringStreamUtil.inputStreamToString(is);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(connection != null){
connection.disconnect();
}
}


三、Post方式:

public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL("http://192.168.23.1:8080/TestProject/PostTest");
connection = (HttpURLConnection) url.openConnection();
// 设置请求方式
connection.setRequestMethod("POST");
// 设置编码格式
connection.setRequestProperty("Charset", "UTF-8");
// 传递自定义参数
connection.setRequestProperty("MyProperty", "this is me!");
// 设置容许输出
connection.setDoOutput(true);

// 上传一张图片|上传参数: byte[] requestBody = new String("name=孙群&age=27").getBytes("UTF-8");
FileInputStream file = new FileInputStream(Environment.getExternalStorageDirectory().getPath()
+ "/Pictures/Screenshots/Screenshot_2015-12-19-08-40-18.png");
OutputStream os = connection.getOutputStream();
int count = 0;
while((count=file.read()) != -1){
os.write(count);
}
os.flush();
os.close();

// 获取返回数据
if(connection.getResponseCode() == 200){
InputStream is = connection.getInputStream();
result = StringStreamUtil.inputStreamToString(is);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(connection!=null){
connection.disconnect();
}
}
};


四、上传APK包大文件:

public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL("http://192.168.23.1:8080/TestProject/FileTest");
connection = (HttpURLConnection) url.openConnection();
// 设置每次传输的流大小,可以有效防止手机因为内存不足崩溃
// 此方法用于在预先不知道内容长度时启用没有进行内部缓冲的 HTTP请求正文的流。
connection.setChunkedStreamingMode(51200); // 128K
// 不使用缓存
connection.setUseCaches(false);
// 设置请求方式
connection.setRequestMethod("POST");
// 设置编码格式
connection.setRequestProperty("Charset", "UTF-8");
// 设置容许输出
connection.setDoOutput(true);

// 上传文件
FileInputStream file = new FileInputStream(Environment.getExternalStorageDirectory().getPath()
+ "/aaaaa/baidu_map.apk");
OutputStream os = connection.getOutputStream();
byte[] b = new byte[1024];
int count = 0;
while((count = file.read(b)) != -1){
os.write(b, 0, count);
}
os.flush();
os.close();

// 获取返回数据
if(connection.getResponseCode() == 200){
InputStream is = connection.getInputStream();
result = StringStreamUtil.inputStreamToString(is);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
if(connection != null){
connection.disconnect();
}
}
};


五、文件断点上传:

安卓端代码:
public class MoreUploadActivity extends Activity {
private TextView mTvMsg;

private String result = "";

private long start = 0;           // 开始读取的位置
private long stop = 1024 * 1024;  // 结束读取的位置
private int times = 0;            //读取次数

private long fileSize = 0;  //文件大小

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_times_upload);

initView();
}

private void initView(){
mTvMsg = (TextView) findViewById(R.id.tv_upload);

try {
FileInputStream file = new FileInputStream(Environment.getExternalStorageDirectory().getPath() + "/aaaaa/baidu_map.apk");
fileSize = file.available();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

new Thread(uploadThread).start();
}

private Thread uploadThread = new Thread(){
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL("http://192.168.23.1:8080/TestProject/MoreUploadTest");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setChunkedStreamingMode(51200);
connection.setUseCaches(false);
// 设置允许输出
connection.setDoOutput(true);
// 设置断点开始,结束位置
connection.setRequestProperty("Range", "bytes=" + start + "-" + stop);

String path = Environment.getExternalStorageDirectory().getPath() + "/aaaaa/baidu_map.apk";
RandomAccessFile file = new RandomAccessFile(path, "rw");
file.seek(start);
byte[] buffer = new byte[1024];
int count = 0;
OutputStream os = connection.getOutputStream();
if(fileSize > 1024*1024){
for(int i=0; i<1024 && count!=-1; i++){
count = file.read(buffer);
os.write(buffer, 0, count);
}
}else{
for(int i=0; i<(fileSize/1024)+1 && count!=-1; i++){
count = file.read(buffer);
os.write(buffer, 0, count);
}
}
os.flush();
os.close();

Log.e("ABC", connection.getResponseCode() + "");
if(connection.getResponseCode() == 200){
result += StringStreamUtil.inputStreamToString(connection.getInputStream()) + "\n";
}

start = stop + 1;
stop += 1024*1024;
fileSize -= 1024*1024;

Message msg = Message.obtain();
msg.what = 0;
uploadHandler.sendMessage(msg);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(connection != null){
connection.disconnect();
}
}
};
};

private Handler uploadHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
if(msg.what == 0){
if(times >= 8){
mTvMsg.setText(result);
}else{
times += 1;
new Thread(uploadThread).start();
mTvMsg.setText(result);
}
}
};
};
}

六、文件断点下载:1、下载重要的是实现技巧: 一:设置断点请求setRequestProperty("Range", "bytes=0-1024");                                 二:通过RandomAccessFile来将下载的字节插入到指定的位置。2、对于输出流的三个方法的对比:    os.write(byte[] buffer);  可能错误,因为你每次读取的数据小于等于1024,但你每次写入的数据仍然是1024, 对图片有一定影响,对安装包绝对是致命的影响。
    os.write(int oneByte);    效率低
    os.write(byte[] buffer, int byteOffset, int byteCount);   效率高,和第二个方法相比有一个数量级的差别(主观上看,有兴趣的可以测几下)。3、实现多线程断点下载大家可以自己思考一下。
/**
* 断点下载
*/
public class MoreTimesActivity extends Activity {
private TextView mTvMsg;

private String result = "";

private long start = 0;
private long stop = 1024 * 1024;

private int times = 0;  // 根据文件大小自己设的,

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_times_download);

initView();
}

private void initView(){
mTvMsg = (TextView) findViewById(R.id.tv_msg);

new Thread(moreThread).start();
}

private Thread moreThread = new Thread(){
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL("http://ftp-apk.pconline.com.cn/ef19af4e28462271af1117efaf868bc2/pub/download/201010/renshengrili_v4.0.04.05.apk");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoInput(true);
// 设置开始下载的位置和结束下载的位置,单位为字节
connection.setRequestProperty("Range", "bytes=" + start + "-" + stop);

String path = Environment.getExternalStorageDirectory().getPath()  + "/aaaaa/baidu_map.apk";
// 断点下载使用的文件对象RandomAccessFile
RandomAccessFile access = new RandomAccessFile(path, "rw");
// 移动指针到开始位置
access.seek(start);
InputStream is = null;
Log.e("ADB----", connection.getResponseCode() + "");
if(connection.getResponseCode() == 206){
is = connection.getInputStream();
int count = 0;
byte[] buffer = new byte[1024];
while((count = is.read(buffer)) != -1){
access.write(buffer, 0, count);
}
}

if(access != null){
access.close();
}
if(is != null){
is.close();
}

start = stop + 1;
stop += 1024*1024;   // 每次下载1M

Message msg = Message.obtain();
msg.what = 0;
result += "文件" + times + "下载成功" + ":" + start + "---" + stop + "\n";
moreHandler.sendMessage(msg);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(connection != null){
connection.disconnect();
}
}
};
};

private Handler moreHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
if(msg.what == 0 && result!=null){
if(times >= 10){
Message msg1 = Message.obtain();
msg1.what = 1;
moreHandler.sendMessage(msg1);
}else{
new Thread(moreThread).start();
times += 1;
}

mTvMsg.setText(result);
}else if(msg.what == 1){
mTvMsg.setText(result);
}
};
};
}


三、Demo讲解

为了演示HttpURLConnection的常见用法,我做了一个App,界面如下所示:

主界面MainActivity有四个按钮,分别表示用GET发送请求、用POST发送键值对数据、用POST发送XML数据以及用POST发送JSON数据,点击对应的按钮会启动NetworkActivity并执行相应的操作。NetworkActivity的源码如下所示,此处先贴出代码,后面会详细说明。
package com.ispring.httpurlconnection;

import android.content.Intent;
import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class NetworkActivity extends AppCompatActivity {

private NetworkAsyncTask networkAsyncTask = new NetworkAsyncTask();

private TextView tvUrl = null;
private TextView tvRequestHeader = null;
private TextView tvRequestBody = null;
private TextView tvResponseHeader = null;
private TextView tvResponseBody = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_network);
tvUrl = (TextView) findViewById(R.id.tvUrl);
tvRequestHeader = (TextView) findViewById(R.id.tvRequestHeader);
tvRequestBody = (TextView) findViewById(R.id.tvRequestBody);
tvResponseHeader = (TextView) findViewById(R.id.tvResponseHeader);
tvResponseBody = (TextView) findViewById(R.id.tvResponseBody);
Intent intent = getIntent();
if (intent != null && intent.getExtras() != null) {
String networkAction = intent.getStringExtra("action");
networkAsyncTask.execute(networkAction);
}
}

//用于进行网络请求的AsyncTask
class NetworkAsyncTask extends AsyncTask<String, Integer, Map<String, Object>> {
//NETWORK_GET表示发送GET请求
public static final String NETWORK_GET = "NETWORK_GET";
//NETWORK_POST_KEY_VALUE表示用POST发送键值对数据
public static final String NETWORK_POST_KEY_VALUE = "NETWORK_POST_KEY_VALUE";
//NETWORK_POST_XML表示用POST发送XML数据
public static final String NETWORK_POST_XML = "NETWORK_POST_XML";
//NETWORK_POST_JSON表示用POST发送JSON数据
public static final String NETWORK_POST_JSON = "NETWORK_POST_JSON";

@Override
protected Map<String, Object> doInBackground(String... params) {
Map<String,Object> result = new HashMap<>();
URL url = null;//请求的URL地址
HttpURLConnection conn = null;
String requestHeader = null;//请求头
byte[] requestBody = null;//请求体
String responseHeader = null;//响应头
byte[] responseBody = null;//响应体
String action = params[0];//http请求的操作类型

try {
if (NETWORK_GET.equals(action)) {
//发送GET请求
url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet?name=孙群&age=27");
conn = (HttpURLConnection) url.openConnection();
//HttpURLConnection默认就是用GET发送请求,所以下面的setRequestMethod可以省略
conn.setRequestMethod("GET");
//HttpURLConnection默认也支持从服务端读取结果流,所以下面的setDoInput也可以省略
conn.setDoInput(true);
//用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
conn.setRequestProperty("action", NETWORK_GET);
//禁用网络缓存
conn.setUseCaches(false);
//获取请求头
requestHeader = getReqeustHeader(conn);
//在对各种参数配置完成后,通过调用connect方法建立TCP连接,但是并未真正获取数据
//conn.connect()方法不必显式调用,当调用conn.getInputStream()方法时内部也会自动调用connect方法
conn.connect();
//调用getInputStream方法后,服务端才会收到请求,并阻塞式地接收服务端返回的数据
InputStream is = conn.getInputStream();
//将InputStream转换成byte数组,getBytesByInputStream会关闭输入流
responseBody = getBytesByInputStream(is);
//获取响应头
responseHeader = getResponseHeader(conn);
} else if (NETWORK_POST_KEY_VALUE.equals(action)) {
//用POST发送键值对数据
url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
conn = (HttpURLConnection) url.openConnection();
//通过setRequestMethod将conn设置成POST方法
conn.setRequestMethod("POST");
//调用conn.setDoOutput()方法以显式开启请求体
conn.setDoOutput(true);
//用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
conn.setRequestProperty("action", NETWORK_POST_KEY_VALUE);
//获取请求头
requestHeader = getReqeustHeader(conn);
//获取conn的输出流
OutputStream os = conn.getOutputStream();
//获取两个键值对name=孙群和age=27的字节数组,将该字节数组作为请求体
requestBody = new String("name=孙群&age=27").getBytes("UTF-8");
//将请求体写入到conn的输出流中
os.write(requestBody);
//记得调用输出流的flush方法
os.flush();
//关闭输出流
os.close();
//当调用getInputStream方法时才真正将请求体数据上传至服务器
InputStream is = conn.getInputStream();
//获得响应体的字节数组
responseBody = getBytesByInputStream(is);
//获得响应头
responseHeader = getResponseHeader(conn);
} else if (NETWORK_POST_XML.equals(action)) {
//用POST发送XML数据
url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
conn = (HttpURLConnection) url.openConnection();
//通过setRequestMethod将conn设置成POST方法
conn.setRequestMethod("POST");
//调用conn.setDoOutput()方法以显式开启请求体
conn.setDoOutput(true);
//用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
conn.setRequestProperty("action", NETWORK_POST_XML);
//获取请求头
requestHeader = getReqeustHeader(conn);
//获取conn的输出流
OutputStream os = conn.getOutputStream();
//读取assets目录下的person.xml文件,将其字节数组作为请求体
requestBody = getBytesFromAssets("person.xml");
//将请求体写入到conn的输出流中
os.write(requestBody);
//记得调用输出流的flush方法
os.flush();
//关闭输出流
os.close();
//当调用getInputStream方法时才真正将请求体数据上传至服务器
InputStream is = conn.getInputStream();
//获得响应体的字节数组
responseBody = getBytesByInputStream(is);
//获得响应头
responseHeader = getResponseHeader(conn);
} else if (NETWORK_POST_JSON.equals(action)) {
//用POST发送JSON数据
url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
conn = (HttpURLConnection) url.openConnection();
//通过setRequestMethod将conn设置成POST方法
conn.setRequestMethod("POST");
//调用conn.setDoOutput()方法以显式开启请求体
conn.setDoOutput(true);
//用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
conn.setRequestProperty("action", NETWORK_POST_JSON);
//获取请求头
requestHeader = getReqeustHeader(conn);
//获取conn的输出流
OutputStream os = conn.getOutputStream();
//读取assets目录下的person.json文件,将其字节数组作为请求体
requestBody = getBytesFromAssets("person.json");
//将请求体写入到conn的输出流中
os.write(requestBody);
//记得调用输出流的flush方法
os.flush();
//关闭输出流
os.close();
//当调用getInputStream方法时才真正将请求体数据上传至服务器
InputStream is = conn.getInputStream();
//获得响应体的字节数组
responseBody = getBytesByInputStream(is);
//获得响应头
responseHeader = getResponseHeader(conn);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//最后将conn断开连接
if (conn != null) {
conn.disconnect();
}
}

result.put("url", url.toString());
result.put("action", action);
result.put("requestHeader", requestHeader);
result.put("requestBody", requestBody);
result.put("responseHeader", responseHeader);
result.put("responseBody", responseBody);
return result;
}

@Override
protected void onPostExecute(Map<String, Object> result) {
super.onPostExecute(result);
String url = (String)result.get("url");//请求的URL地址
String action = (String) result.get("action");//http请求的操作类型
String requestHeader = (String) result.get("requestHeader");//请求头
byte[] requestBody = (byte[]) result.get("requestBody");//请求体
String responseHeader = (String) result.get("responseHeader");//响应头
byte[] responseBody = (byte[]) result.get("responseBody");//响应体

//更新tvUrl,显示Url
tvUrl.setText(url);

//更新tvRequestHeader,显示请求头
if (requestHeader != null) {
tvRequestHeader.setText(requestHeader);
}

//更新tvRequestBody,显示请求体
if(requestBody != null){
try{
String request = new String(requestBody, "UTF-8");
tvRequestBody.setText(request);
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}
}

//更新tvResponseHeader,显示响应头
if (responseHeader != null) {
tvResponseHeader.setText(responseHeader);
}

//更新tvResponseBody,显示响应体
if (NETWORK_GET.equals(action)) {
String response = getStringByBytes(responseBody);
tvResponseBody.setText(response);
} else if (NETWORK_POST_KEY_VALUE.equals(action)) {
String response = getStringByBytes(responseBody);
tvResponseBody.setText(response);
} else if (NETWORK_POST_XML.equals(action)) {
//将表示xml的字节数组进行解析
String response = parseXmlResultByBytes(responseBody);
tvResponseBody.setText(response);
} else if (NETWORK_POST_JSON.equals(action)) {
//将表示json的字节数组进行解析
String response = parseJsonResultByBytes(responseBody);
tvResponseBody.setText(response);
}
}

//读取请求头
private String getReqeustHeader(HttpURLConnection conn) {
//https://github.com/square/okhttp/blob/master/okhttp-urlconnection/src/main/java/okhttp3/internal/huc/HttpURLConnectionImpl.java#L236
Map<String, List<String>> requestHeaderMap = conn.getRequestProperties();
Iterator<String> requestHeaderIterator = requestHeaderMap.keySet().iterator();
StringBuilder sbRequestHeader = new StringBuilder();
while (requestHeaderIterator.hasNext()) {
String requestHeaderKey = requestHeaderIterator.next();
String requestHeaderValue = conn.getRequestProperty(requestHeaderKey);
sbRequestHeader.append(requestHeaderKey);
sbRequestHeader.append(":");
sbRequestHeader.append(requestHeaderValue);
sbRequestHeader.append("\n");
}
return sbRequestHeader.toString();
}

//读取响应头
private String getResponseHeader(HttpURLConnection conn) {
Map<String, List<String>> responseHeaderMap = conn.getHeaderFields();
int size = responseHeaderMap.size();
StringBuilder sbResponseHeader = new StringBuilder();
for(int i = 0; i < size; i++){
String responseHeaderKey = conn.getHeaderFieldKey(i);
String responseHeaderValue = conn.getHeaderField(i);
sbResponseHeader.append(responseHeaderKey);
sbResponseHeader.append(":");
sbResponseHeader.append(responseHeaderValue);
sbResponseHeader.append("\n");
}
return sbResponseHeader.toString();
}

//根据字节数组构建UTF-8字符串
private String getStringByBytes(byte[] bytes) {
String str = "";
try {
str = new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return str;
}

//从InputStream中读取数据,转换成byte数组,最后关闭InputStream
private byte[] getBytesByInputStream(InputStream is) {
byte[] bytes = null;
BufferedInputStream bis = new BufferedInputStream(is);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(baos);
byte[] buffer = new byte[1024 * 8];
int length = 0;
try {
while ((length = bis.read(buffer)) > 0) {
bos.write(buffer, 0, length);
}
bos.flush();
bytes = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}

return bytes;
}

//根据文件名,从asserts目录中读取文件的字节数组
private byte[] getBytesFromAssets(String fileName){
byte[] bytes = null;
AssetManager assetManager = getAssets();
InputStream is = null;
try{
is = assetManager.open(fileName);
bytes = getBytesByInputStream(is);
}catch (IOException e){
e.printStackTrace();
}
return bytes;
}

//将表示xml的字节数组进行解析
private String parseXmlResultByBytes(byte[] bytes) {
InputStream is = new ByteArrayInputStream(bytes);
StringBuilder sb = new StringBuilder();
List<Person> persons = XmlParser.parse(is);
for (Person person : persons) {
sb.append(person.toString()).append("\n");
}
return sb.toString();
}

//将表示json的字节数组进行解析
private String parseJsonResultByBytes(byte[] bytes){
String jsonString = getStringByBytes(bytes);
List<Person> persons = JsonParser.parse(jsonString);
StringBuilder sb = new StringBuilder();
for (Person person : persons) {
sb.append(person.toString()).append("\n");
}
return sb.toString();
}

}
}
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
这个App是用来发送http请求的客户端,除此之外,我还创建了一个JSP的WebProject作为服务端,用Servlet对客户端发来的请求进行处理,Servlet的代码如下所示:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Enumeration;

@WebServlet(name = "MyServlet")
public class MyServlet extends HttpServlet {
//GET请求
private static final String NETWORK_GET = "NETWORK_GET";
//用POST发送键值对
private static final String NETWORK_POST_KEY_VALUE = "NETWORK_POST_KEY_VALUE";
//用POST发送XML数据
private static final String NETWORK_POST_XML = "NETWORK_POST_XML";
//用POST发送JSON数据
private static final String NETWORK_POST_JSON = "NETWORK_POST_JSON";

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String action = request.getHeader("action");
//将输入与输出都设置为UTF-8编码
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain;charset=UTF-8");
//response.setHeader("content-type","text/plain;charset=UTF-8");
if(NETWORK_GET.equals(action) || NETWORK_POST_KEY_VALUE.equals(action)){
//对于NETWORK_GET和NETWORK_POST_KEY_VALUE,遍历键值对,并将键值对重新写回到输出结果中
Enumeration<String> parameterNames = request.getParameterNames();
PrintWriter writer = response.getWriter();
while(parameterNames.hasMoreElements()){
String name = parameterNames.nextElement();
String value = request.getParameter(name);
if(request.getMethod().toUpperCase().equals("GET")){
//GET请求需要进行编码转换,POST不需要
value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
}
writer.write(name + "=" + value + "\n");
}
writer.flush();
writer.close();
}else if(NETWORK_POST_XML.equals(action) || NETWORK_POST_JSON.equals(action)){
//对于NETWORK_POST_XML和NETWORK_POST_JSON,将请求体重新写入到响应体的输出流中
//通过request.getInputStream()得到http请求的请求体
BufferedInputStream bis  = new BufferedInputStream(request.getInputStream());
//通过response.getOutputStream()得到http请求的响应体
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
byte[] buffer = new byte[1024 * 8];
int length = 0;
while ( (length = bis.read(buffer)) > 0){
bos.write(buffer, 0, length);
}
bos.flush();
bos.close();
bis.close();
}else{
PrintWriter writer = response.getWriter();
writer.write("非法的请求头: action");
writer.flush();
writer.close();
}
}
}
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
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

发送GET请求

由于网络请求耗时而且会阻塞当前线程,所以我们将发送http请求的操作都放到NetworkAsyncTask中,NetworkAsyncTask是继承自AsyncTask。点击”GET”按钮后,界面如下所示: 


GET请求是最简单的http请求,其发送请求的代码如下所示:
if (NETWORK_GET.equals(action)) {
//发送GET请求
url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet?name=孙群&age=27");
conn = (HttpURLConnection) url.openConnection();
//HttpURLConnection默认就是用GET发送请求,所以下面的setRequestMethod可以省略
conn.setRequestMethod("GET");
//HttpURLConnection默认也支持从服务端读取结果流,所以下面的setDoInput也可以省略
conn.setDoInput(true);
//用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
conn.setRequestProperty("action", NETWORK_GET);
//禁用网络缓存
conn.setUseCaches(false);
//获取请求头
requestHeader = getReqeustHeader(conn);
//在对各种参数配置完成后,通过调用connect方法建立TCP连接,但是并未真正获取数据
//conn.connect()方法不必显式调用,当调用conn.getInputStream()方法时内部也会自动调用connect方法
conn.connect();
//调用getInputStream方法后,服务端才会收到请求,并阻塞式地接收服务端返回的数据
InputStream is = conn.getInputStream();
//将InputStream转换成byte数组,getBytesByInputStream会关闭输入流
responseBody = getBytesByInputStream(is);
//获取响应头
responseHeader = getResponseHeader(conn);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
上面的注释写的比较详细了,此处对代码进行一下简单说明。我们在URL的后面添加了
?name=孙群&age=27
,这样就相当于添加了两个键值对,其形式如
?key1=value1&key2=value2&key3=value3
,在
?
后面添加键值对,键和值之间用
=
相连,键值对之间用
&
分隔,服务端可以读取这些键值对信息。
HttpURLConnection默认就是用GET发送请求,当然也可以用conn.setRequestMethod(“GET”)将其显式地设置为GET请求。
通过setRequestProperty方法可以设置请求头,既可以是标准的请求头,也可以是自定义的请求头,此处我们设置了自定义的请求头action,用于服务端判断请求的类型。
GET请求容易被缓存,我们可以用conn.setUseCaches(false)禁用缓存。
通过调用connect方法可以让客户端和服务器之间建立TCP连接,建立连接之后不会立即传输数据,只是表示处于connected状态了。该方法不必显式调用,因为在之后的getInputStream方法中会隐式地调用该方法。
调用HttpURLConnection的getInputStream()方法可以获得响应结果的输入流,即服务器向客户端输出的信息,需要注意的是,getInputStream()方法的调用必须在一系列的set方法之后进行。
然后在方法getBytesByInputStream中,通过输入流的read方法得到字节数组,read方法是阻塞式的,每read一次,其实就是从服务器上下载一部分数据,直到将服务器的输出全部下载完成,这样就得到响应体responseBody了。
我们可以通过getReqeustHeader方法读取请求头,代码如下所示:
//读取请求头
private String getReqeustHeader(HttpURLConnection conn) {
Map<String, List<String>> requestHeaderMap = conn.getRequestProperties();
Iterator<String> requestHeaderIterator = requestHeaderMap.keySet().iterator();
StringBuilder sbRequestHeader = new StringBuilder();
while (requestHeaderIterator.hasNext()) {
String requestHeaderKey = requestHeaderIterator.next();
String requestHeaderValue = conn.getRequestProperty(requestHeaderKey);
sbRequestHeader.append(requestHeaderKey);
sbRequestHeader.append(":");
sbRequestHeader.append(requestHeaderValue);
sbRequestHeader.append("\n");
}
return sbRequestHeader.toString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
由上可以看出,以上方法主要还是调用了HttpURLConnection的方法getRequestProperty获取请求头,需要注意的是getRequestProperty方法执行时,客户端和服务器之间必须还未建立TCP连接,即还没有调用connect方法,在connected之后执行getRequestProperty会抛出异常,详见https://github.com/square/okhttp/blob/master/okhttp-urlconnection/src/main/java/okhttp3/internal/huc/HttpURLConnectionImpl.java#L236
我们还可以通过getResponseHeader方法获取响应头,代码如下所示:
//读取响应头
private String getResponseHeader(HttpURLConnection conn) {
Map<String, List<String>> responseHeaderMap = conn.getHeaderFields();
int size = responseHeaderMap.size();
StringBuilder sbResponseHeader = new StringBuilder();
for(int i = 0; i < size; i++){
String responseHeaderKey = conn.getHeaderFieldKey(i);
String responseHeaderValue = conn.getHeaderField(i);
sbResponseHeader.append(responseHeaderKey);
sbResponseHeader.append(":");
sbResponseHeader.append(responseHeaderValue);
sbResponseHeader.append("\n");
}
return sbResponseHeader.toString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
通过方法getHeaderFieldKey可以获得响应头的key值,通过方法getHeaderField可以获得响应头的value值。
在后台的Servelt中将键值对信息重新原样写入到客户端,服务端的代码如下所示: 
if(NETWORK_GET.equals(action) || NETWORK_POST_KEY_VALUE.equals(action)){
//对于NETWORK_GET和NETWORK_POST_KEY_VALUE,遍历键值对,并将键值对重新写回到输出结果中
Enumeration<String> parameterNames = request.getParameterNames();
PrintWriter writer = response.getWriter();
while(parameterNames.hasMoreElements()){
String name = parameterNames.nextElement();
String value = request.getParameter(name);
if(request.getMethod().toUpperCase().equals("GET")){
//GET请求需要进行编码转换,POST不需要
value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
}
writer.write(name + "=" + value + "\n");
}
writer.flush();
writer.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在接收到服务端返回的数据后,在AsyncTask的onPostExecute方法中会将Url、请求头、响应头、响应体的值展示在UI上。

用POST发送键值对数据

点击”POST KEY VALUE”按钮,可以用POST发送键值对数据,界面如下所示: 


代码如下所示:
if (NETWORK_POST_KEY_VALUE.equals(action)) {
//用POST发送键值对数据
url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
conn = (HttpURLConnection) url.openConnection();
//通过setRequestMethod将conn设置成POST方法
conn.setRequestMethod("POST");
//调用conn.setDoOutput()方法以显式开启请求体
conn.setDoOutput(true);
//用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
conn.setRequestProperty("action", NETWORK_POST_KEY_VALUE);
//获取请求头
requestHeader = getReqeustHeader(conn);
//获取conn的输出流
OutputStream os = conn.getOutputStream();
//获取两个键值对name=孙群和age=27的字节数组,将该字节数组作为请求体
requestBody = new String("name=孙群&age=27").getBytes("UTF-8");
//将请求体写入到conn的输出流中
os.write(requestBody);
//记得调用输出流的flush方法
os.flush();
//关闭输出流
os.close();
//当调用getInputStream方法时才真正将请求体数据上传至服务器
InputStream is = conn.getInputStream();
//获得响应体的字节数组
responseBody = getBytesByInputStream(is);
//获得响应头
responseHeader = getResponseHeader(conn);
}
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
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
使用POST发送请求的代码与用GET发送请求的代码大部分类似,我们只对其中不同的地方做下说明。需要通过setRequestMethod将conn设置成POST方法。
如果想用POST发送请求体,那么需要调用setDoOutput方法,将其设置为true。
通过conn.getOutputStream()获得输出流,可以向输出流中写入请求体,最后记得调用输出流的flush方法,注意此时并没有真正将请求体发送到服务器端。
当调用getInputStream方法后,才真正将请求体的内容发送到服务器。
在我们的服务器端的Servlet中,在接收到POST请求发送的键值对数据后,也只是简单地将键值对数据原样写入给客户端,具体代码参见上文,不再赘述。

用POST发送XML数据

点击”POST XML”按钮,可以用POST发送XML数据,界面如下所示: 


代码如下所示:
if (NETWORK_POST_XML.equals(action)) {
//用POST发送XML数据
url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
conn = (HttpURLConnection) url.openConnection();
//通过setRequestMethod将conn设置成POST方法
conn.setRequestMethod("POST");
//调用conn.setDoOutput()方法以显式开启请求体
conn.setDoOutput(true);
//用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
conn.setRequestProperty("action", NETWORK_POST_XML);
//获取请求头
requestHeader = getReqeustHeader(conn);
//获取conn的输出流
OutputStream os = conn.getOutputStream();
//读取assets目录下的person.xml文件,将其字节数组作为请求体
requestBody = getBytesFromAssets("person.xml");
//将请求体写入到conn的输出流中
os.write(requestBody);
//记得调用输出流的flush方法
os.flush();
//关闭输出流
os.close();
//当调用getInputStream方法时才真正将请求体数据上传至服务器
InputStream is = conn.getInputStream();
//获得响应体的字节数组
responseBody = getBytesByInputStream(is);
//获得响应头
responseHeader = getResponseHeader(conn);
}
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
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
上面的代码与用POST发送键值对的代码很相似,对其进行简单说明。上述代码通过getBytesFromAssets方法读取了assets目录下的person.xml文件,将xml文件的字节流作为请求体requestBody,然后将该请求体发送到服务器。
person.xml文件如下所示:
<?xml version="1.0" encoding="utf-8"?>
<persons>
<person id="101">
<name>张三</name>
<age>27</age>
</person>
<person id="102">
<name>李四</name>
<age>28</age>
</person>
</persons>
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
<person>
标签对应着
Person
类,
Person
类代码如下所示:
package com.ispring.httpurlconnection;

public class Person {
private String id = "";
private String name = "";
private int age = 0;

public String getId(){
return id;
}

public void setId(String id){
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return new StringBuilder().append("name:").append(getName()).append(", age:").append(getAge()).toString();
}
}
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
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

服务端接收到客户端发送来的XML数据之后,只是简单的将其原样写回到客户端,即客户端接收到的响应体还是原来的XML数据,然后客户端通过parseXmlResultByBytes方法对该XML数据进行解析,将字节数组转换成
List<Person>
,parseXmlResultByBytes代码如下所示: 
//将表示xml的字节数组进行解析
private String parseXmlResultByBytes(byte[] bytes) {
InputStream is = new ByteArrayInputStream(bytes);
StringBuilder sb = new StringBuilder();
List<Person> persons = XmlParser.parse(is);
for (Person person : persons) {
sb.append(person.toString()).append("\n");
}
return sb.toString();
}
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
该方法使用了自定义的XmlParser类对XML数据进行解析,其源码如下所示:
package com.ispring.httpurlconnection;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

public class XmlParser {

public static List<Person> parse(InputStream is) {
List<Person> persons = new ArrayList<>();
try{
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
PersonHandler personHandler = new PersonHandler();
parser.parse(is, personHandler);
persons = personHandler.getPersons();
}catch (Exception e){
e.printStackTrace();
}
return persons;
}

static class PersonHandler extends DefaultHandler {
private List<Person> persons;
private Person temp;
private StringBuilder sb;

public List<Person> getPersons(){
return persons;
}

@Override
public void startDocument() throws SAXException {
super.startDocument();
persons = new ArrayList<>();
sb = new StringBuilder();
}

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
sb.setLength(0);
if(localName.equals("person")){
temp = new Person();
int length = attributes.getLength();
for(int i = 0; i < length; i++){
String name = attributes.getLocalName(i);
if(name.equals("id")){
String value = attributes.getValue(i);
temp.setId(value);
}
}
}
}

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
sb.append(ch, start, length);
}

@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
if(localName.equals("name")){
String name = sb.toString();
temp.setName(name);
}else if(localName.equals("age")){
int age = Integer.parseInt(sb.toString());
temp.setAge(age);
}else if(localName.equals("person")){
persons.add(temp);
}
}
}
}
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
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

用POST发送JSON数据

点击”POST JSON”按钮,可以用POST发送JSON数据,界面如下所示: 


代码如下所示:
if (NETWORK_POST_JSON.equals(action)) {
//用POST发送JSON数据
url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
conn = (HttpURLConnection) url.openConnection();
//通过setRequestMethod将conn设置成POST方法
conn.setRequestMethod("POST");
//调用conn.setDoOutput()方法以显式开启请求体
conn.setDoOutput(true);
//用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
conn.setRequestProperty("action", NETWORK_POST_JSON);
//获取请求头
requestHeader = getReqeustHeader(conn);
//获取conn的输出流
OutputStream os = conn.getOutputStream();
//读取assets目录下的person.json文件,将其字节数组作为请求体
requestBody = getBytesFromAssets("person.json");
//将请求体写入到conn的输出流中
os.write(requestBody);
//记得调用输出流的flush方法
os.flush();
//关闭输出流
os.close();
//当调用getInputStream方法时才真正将请求体数据上传至服务器
InputStream is = conn.getInputStream();
//获得响应体的字节数组
responseBody = getBytesByInputStream(is);
//获得响应头
responseHeader = getResponseHeader(conn);
}
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
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
上面的代码与用POST发送XML的代码很相似,对其进行简单说明。上述代码通过getBytesFromAssets方法读取了assets目录下的person.json文件,将json文件的字节流作为请求体requestBody,然后将该请求体发送到服务器。
person.json文件如下所示:
{
"persons": [{
"id": "101",
"name":"张三",
"age":27
}, {
"id": "102",
"name":"李四",
"age":28
}]
}
1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
persons数组中的每个元素都对应着一个
Person
对象。
服务端接收到客户端发送来的JSON数据之后,只是简单的将其原样写回到客户端,即客户端接收到的响应体还是原来的JSON数据,然后客户端通过parseJsonResultByBytes方法对该XML数据进行解析,将字节数组转换成List,parseXmlResultByBytes代码如下所示:
//将表示json的字节数组进行解析
private String parseJsonResultByBytes(byte[] bytes){
String jsonString = getStringByBytes(bytes);
List<Person> persons = JsonParser.parse(jsonString);
StringBuilder sb = new StringBuilder();
for (Person person : persons) {
sb.append(person.toString()).append("\n");
}
return sb.toString();
}
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
该方法又使用了自定义的JsonParset类,源码如下所示:
package com.ispring.httpurlconnection;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class JsonParser {
public static List<Person> parse(String jsonString){
List<Person> persons = new ArrayList<>();

try{
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("persons");
int length = jsonArray.length();
for(int i = 0; i < length; i++){
JSONObject personObject = jsonArray.getJSONObject(i);
String id = personObject.getString("id");
String name = personObject.getString("name");
int age = personObject.getInt("age");
Person person = new Person();
person.setId(id);
person.setName(name);
person.setAge(age);
persons.add(person);
}
}catch (JSONException e){
e.printStackTrace();
}

return persons;
}
}
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
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

其他

如果Http请求体的数据很大,就可以认为该请求主要是完成数据上传的作用;如果响应体的数据很大,就可以认为该请求主要完成数据下载的作用。
上面我们通过demo演示了如何上传XML文件和JSON文件,并对二者进行解析。在上传的过程中,Android要写入Content-Length这个请求头,Content-Length就是请求体的字节长度,注意是字节长度,而不是字符长度(汉字等会占用两个字节)。默认情况下,Android为了得到Content-Length的长度,Android会把请求体放到内存中的,直到输出流调用了close方法后,才会读取内存中请求体的字节长度,将其作为请求头Content-Length。当要上传的请求体很大时,这会非常占用内存,为此Android提供了两个方法来解决这个问题。setFixedLengthStreamingMode (int contentLength) 
如果请求体的大小是知道的,那么可以调用HttpURLConnection的setFixedLengthStreamingMode (int contentLength) 方法,该方法会告诉Android要传输的请求头Content-Length的大小,这样Android就无需读取整个请求体的大小,从而不必一下将请求体全部放到内存中,这样就避免了请求体占用巨大内存的问题。
setChunkedStreamingMode (int chunkLength) 
如果请求体的大小不知道,那么可以调用setChunkedStreamingMode (int chunkLength)方法。该方法将传输的请求体分块传输,即将原始的数据分成多个数据块,chunkLength表示每块传输的字节大小。比如我们要传输的请求体大小是10M,我们将chunkLength设置为1024 * 1024 byte,即1M,那么Android会将请求体分10次传输,每次传输1M,具体的传输规则是:每次传输一个数据块时,首先在一行中写明该数据块的长度,比如1024 * 1024,然后在后面的一行中写入要传输的数据块的字节数组,再然后是一个空白行,这样第一数据块就这样传输,在空白行之后就是第二个数据块的传输,与第一个数据块的格式一样,直到最后没有数据块要传输了,就在用一行写明要传输的字节为0,这样在服务器端就知道读取完了整个请求体了。如果设置的chunkLength的值为0,那么表示Android会使用默认的一个值作为实际的chunkLength。使用setChunkedStreamingMode方法的前提是服务器支持分块数据传输,分块数据传输是从HTTP 1.1开始支持的,所以如果你的服务器只支持HTTP 1.0的话,那么不能使用setChunkedStreamingMode方法。

希望本文对大家使用HttpURLConnection有所帮助!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐