Bootstrap

android httpurlconnection 参数,Android HttpURLConnection详解

最近有一个项目需要重构网络部分代码,由于之前的网络部分都已经封装好,直接调用接口就行,重构的时候才发现,好多东西已经忘了,现在给大家总结出来,有需要的朋友可以拿走,文章的最后会有demo工程。

HttpURLConnection

早些时候其实我们都习惯性使用HttpClient,但是后来Android6.0之后不再支持HttpClient,需要添加Apache的jar才行,所以,就有很多开发者放弃使用HttpClient了,HttpURLConnection毕竟是标准Java接口(java.net) ,适配性还是很强的。

准备工作

在开始使用之前,我们需要知道网络请求都需要一些什么参数。这里罗列一些常用的参数:

url 请求的地址,这个不用说了,肯定是必须的

请求方式:GET POST还有DELETE,最常用的还是GET和POST

加密规则,这个当然是根据需要可有可无的

header 请求头

参数 需要传递的参数

文件 你可能需要通过网络上传一个文件

知道了这些,我们可以自己定义一个接口:

public interface IRequest {

public String getBaseUrl();

public String getMethod();

public IEncrypt getEncrypt();

public HashMap getParam();

public Map getFilePair();

public Map getHeaders();

}

其中FilePair是:

public class FilePair{

String mFileName;

byte[] mBinaryData;

public FilePair(String fileName, byte[] data) {

this.mFileName = fileName;

this.mBinaryData = data;

}

}

构建这个类,是为了上传文件的时候使用方便。

有了这个接口,我们进行网络请求只需要传递这个接口即可,如果有新的参数,只需要增加接口中的方法即可,不需要改变网络核心的代码。

GET请求

get是用于信息获取的,就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。

他的请求方式是将参数拼接在url中的,比如你请求的地址是http://xxx,参数是name = aa,那么拼接后应该是http://xxx?name=aa

所以我们可以这样处理:

public static String get(IRequest request) {

InputStream inputStream = null;

HttpURLConnection httpURLConnection = null;

try {

URL url = new URL(buildGetUrl(request.getBaseUrl(), request.getParam(), request.getEncrypt()));

openUrlConnection(url,httpURLConnection);

normalSetting(httpURLConnection, Method.GET, request.getHeaders());

if (httpURLConnection == null) {

return null;

}

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {

inputStream = httpURLConnection.getInputStream();

String contentEncoding = httpURLConnection.getContentEncoding();

InputStream stream = null;

try {

stream = wrapStream(contentEncoding, inputStream);

String data = convertStreamToString(stream);

return data;

} catch (IOException e) {

return "";

} finally {

closeQuietly(stream);

}

}

return null;

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

return "";

}

首先需要根据参数拼接url:

private static String buildGetUrl(String urlPath, Map params, IEncrypt encrypt) {

if (TextUtils.isEmpty(urlPath) || params == null || params.size() == 0) {

return urlPath;

}

if (!urlPath.endsWith("?")) {

urlPath += "?";

}

String paramsStr = buildGetParams(params);

if (encrypt != null) {

paramsStr = encrypt.encrypt(urlPath, params);

}

StringBuilder sbUrl = new StringBuilder(urlPath);

sbUrl.append(paramsStr);

return sbUrl.toString();

}

private static String buildGetParams(Map params) {

StringBuilder sb = new StringBuilder();

Set keys = params.keySet();

for (String key : keys) {

if (params.get(key) == null) {

continue;

}

sb = sb.append(key + "=" + URLEncoder.encode(params.get(key).toString()) + "&");

}

String paramsStr = sb.substring(0, sb.length() - 1).toString();

return paramsStr;

}

这里可以看出可以根据encrypt进行加密,encrypt是实现的加密和解密接口:

public interface IEncrypt {

public String encrypt(String src);

public String dencrypt(String src);

}

加密之后,通过HttpURLConnection进行请求即可。

如果不需要加密,可以将这个参数设置为空,或者直接实现,返回原字符串即可。

httpURLConnection.getResponseCode()是返回的响应码,当为200时是标志请求成功了,这里需要注意的是如果返回301,或者是302,是由于链接重定向的问题造成的,我们可以通过String location =httpURLConnection.getHeaderField("Location");获取重定向的网址进行重新请求。其中有个normalSetting,这个我们放在后面说明。

POST

POST表示可能修改变服务器上的资源的请求,比如我们发一个帖子到服务器,这时候就用到了post请求,他会改变服务器中的存储资源。

POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的 HTTP 请求满足上面的格式就可以。 所以我们必须告诉服务端你是用的什么编码方式。服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。

application/x-www-form-urlencoded

这应该是最常见的 POST 提交数据的方式了。浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。请求类似于下面这样(无关的请求头在本文中都省略掉了):

POST http://www.example.com HTTP/1.1

Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

我们需要做的是

Content-Type 被指定为 application/x-www-form-urlencoded

其次,提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。代码如下:

httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

Uri.Builder builder = new Uri.Builder();

builder.appendQueryParameter("content", request.getMessage());

String query = builder.build().getEncodedQuery();

outputStream = new DataOutputStream(httpURLConnection.getOutputStream());

outputStream.write(query.getBytes());

multipart/form-data

这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 form 的 enctyped 等于这个值。直接来看一个请求示例:

POST http://www.example.com HTTP/1.1

Content-Type:multipart/form-data; boundary=----xxxxx

------xxxxx

Content-Disposition: form-data; name="text"

title

------xxxxx

Content-Disposition: form-data; name="file"; filename="chrome.png"

Content-Type: image/png

PNG ... content of chrome.png ...

------xxxxx--

首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 mutipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary 开始,紧接着内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary-- 标示结束

看下代码:

httpURLConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

outputStream = httpURLConnection.getOutputStream();

addBodyParams(request.getParam(),request.getFilePair(), outputStream, boundary);

其中写入数据的方法较为繁琐:

private static void addBodyParams(HashMap map, Map filePair, OutputStream outputStream, String boundary) throws IOException {

boolean didWriteData = false;

StringBuilder stringBuilder = new StringBuilder();

Map bodyPair =map;

Set keys = bodyPair.keySet();

for (String key : keys) {

if (bodyPair.get(key) != null) {

addFormField(stringBuilder, key, bodyPair.get(key).toString(), boundary);

}

}

if (stringBuilder.length() > 0) {

didWriteData = true;

outputStream = new DataOutputStream(outputStream);

outputStream.write(stringBuilder.toString().getBytes());

}

// upload files like POST files to server

if (filePair != null && filePair.size() > 0) {

Set fileKeys = filePair.keySet();

for (String key : fileKeys) {

FilePair pair = filePair.get(key);

byte[] data = pair.mBinaryData;

if (data == null || data.length < 1) {

continue;

} else {

didWriteData = true;

addFilePart(pair.mFileName, data, boundary, outputStream);

}

}

}

if (didWriteData) {

finishWrite(outputStream, boundary);

}

}

private static void addFormField(StringBuilder writer, final String name, final String value, String boundary) {

writer.append("--").append(boundary).append(END)

.append("Content-Disposition: form-data; name=\"").append(name)

.append("\"").append(END)

.append("Content-Type: text/plain; charset=").append("UTF-8")

.append(END).append(END).append(value).append(END);

}

private static void addFilePart(final String fieldName, byte[] data, String boundary, OutputStream outputStream)

throws IOException {

StringBuilder stringBuilder = new StringBuilder();

stringBuilder.append("--").append(boundary).append(END)

.append("Content-Disposition: form-data; name=\"")

.append("pic").append("\"; filename=\"").append(fieldName)

.append("\"").append(END).append("Content-Type: ")

.append("application/octet-stream").append(END)

.append("Content-Transfer-Encoding: binary").append(END)

.append(END);

outputStream.write(stringBuilder.toString().getBytes());

outputStream.write(data);

outputStream.write(END.getBytes());

}

其它

除了上面提到过的两种方式,还有application/json 以及text/xml ,这两种在移动端开发很少使用,不再过多介绍。

post代码

public static String post(IRequest request) {

String boundary = UUID.randomUUID().toString();

HttpURLConnection httpURLConnection = null;

OutputStream outputStream = null;

InputStream inputStream = null;

URL url = null;

try {

url = new URL(request.getBaseUrl());

openUrlConnection(url,httpURLConnection);

normalSetting(httpURLConnection,Method.POST,request.getHeaders());

if (request.getParam() != null && request.getParam().size() > 0) {

httpURLConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

outputStream = httpURLConnection.getOutputStream();

addBodyParams(request.getParam(),request.getFilePair(), outputStream, boundary);

} else {

httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

Uri.Builder builder = new Uri.Builder();

builder.appendQueryParameter("content", request.getMessage());

String query = builder.build().getEncodedQuery();

outputStream = new DataOutputStream(httpURLConnection.getOutputStream());

outputStream.write(query.getBytes());

}

outputStream.flush();

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {

inputStream = httpURLConnection.getInputStream();

String contentEncoding = httpURLConnection.getContentEncoding();

InputStream stream = wrapStream(contentEncoding, inputStream);

String data = convertStreamToString(stream);

return data;

}

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

通用配置介绍

private static void normalSetting(HttpURLConnection urlConnection, Method method, Map mHeaders) throws ProtocolException {

urlConnection.setConnectTimeout(connectionTimeOut);

urlConnection.setReadTimeout(readSocketTimeOut);

urlConnection.setRequestMethod(method.toString());

if (method == Method.GET) {

urlConnection.setRequestProperty("Accept-Encoding", "gzip");

if (mHeaders != null && mHeaders.size() > 0) {

Set stringKeys = mHeaders.keySet();

for (String key : stringKeys) {

urlConnection.setRequestProperty(key, mHeaders.get(key));

}

}

} else if (method == Method.POST) {

urlConnection.setDoOutput(true);

urlConnection.setDoInput(true);

}

}

其中

setConnectTimeout:设置连接主机超时(单位:毫秒)

setReadTimeout:设置从主机读取数据超时(单位:毫秒)

Accept-Encoding HTTP Header中Accept-Encoding 是浏览器发给服务器,声明浏览器支持的编码类型

setDoOutput(false);以后就可以使用 httpURLConnection.getOutputStream().write()

setDoInput(true);以后就可以使用 httpURLConnection.getInputStream().read();

参考demo

这个demo是我根据自己项目中用到的进行整理的,可能有些情况考虑的不是很全面,但是基本思路就是这个样子,用到的同学可以参考:

DEMO

;