概述
二月春风似剪刀,在这把剪刀的裁剪下,三月里春天正式拉开了她得帷幕,看到武大学妹发的樱花照片,正是美得无法收拾,令人陶醉,先给大家上两张,共同欣赏
来张近的:
再来张特写
好一只张可爱的乌龟:
连池塘里面的乌龟,都被这美景诱惑,爬上了水面,哈哈。。
时光荏苒,一转眼离开武大已经5年了,但是当我看到这些图的时候,我的思绪一下就回到了5年前,樱顶、梅园、情人坡、奥场… 种种场景历历在目,好怀念那所大学,下辈子,我愿意生在武大,长在武大,学在武大,老在武大…
好像越写越偏离主题了是不是,今天不是来跟大家分享技术的吗,哈哈。 好吧,还是先整理下思绪,开始正题吧,其实说不上分享,共同学习吧,我们知道樱花开了不是吗,但是想我这种人在杭州却想观赏武大樱花的朋友,尤其是像我这种毕业多年的校友们,肯定想一睹樱花的美丽,不要急,今天百度推出了樱花直播,朋友们,只需要在百度搜索里面搜索武大樱花节,就可以直接观看到武大樱花,从多种角度进行观赏,如果运气好的话,还可以看到很多漂亮的MM哦,哈哈,互联网给大家带来的实惠实在是明显了,我们今天要聊的话题其实跟樱花直播也有关系哦,我们知道在百度里面搜索的时候首先是不是要发一个HTTP请求,是不是,如果没有这个请求,我们怎么能拿到数据,或的直播的接口呢?哈哈关系是有点儿牵强,废话说太多了,让我们切入正题吧。
转载请注明出处:http://blog.csdn.net/qinjunni2014/article/details/44570473
互联网对世界的影响越来越大,如今任何事物都可以跟互联网扯上关系了,连克强总理都在两会上提出了“互联网+”的概念,相信如今已经全面进入了互联网改造社会的时代,互联网是基于网络请求的,而Http请求就是网络请求中用的最多的一个请求,今天我们就来浅谈一下HTTP请求如何在Android中合理的使用。
1. HttpUrlConnection
Android中发送http请求有两种方法,一种是使用HttpClient,另外一种就是HttpUrlConnection,但是如今使用最多的是HttpUrlConnection,因为它更轻量,而且对缓存和安全的支持比较好。我们先来看一个基于HttpUrlConnection建立http请求最简单的例子,
String geturl = "http://www.baidu.com;
try {
URL getUrl = new URL(geturl);
HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection();
// 进行连接,但是实际上get request要在下一句的connection.getInputStream()函数中才会真正发到
// 服务器
connection.connect();
// 取得输入流,并使用Reader读取
BufferedReader reader = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
System.out.println("=============================");
System.out.println("Contents of get request");
System.out.println("=============================");
String line;
while ((line = reader.readLine()) != null) {
Log.e("Junli", line);
}
reader.close();
// 断开连接
connection.disconnect();
System.out.println("=============================");
System.out.println("Contents of get request ends");
System.out.println("=============================");
} catch (IOException e) {
e.printStackTrace();
}
我们使用Get请求,去获得百度的主页,只需要简单地五步:
- new URL(String url)创建一个URL对象
- openConnection URL对象打开连接,获得一个HttpUrlConnection
- connect, 建立连接,我们知道http是基于Tcp的,这一步只是建立起Tcp连接,而并没有真正发http请求
- connection.getInputStream获取输入流,这一步才会真正去发request,拿到相应。
这个例子确实很简单,我们来看一个稍微复杂点的:
public void doPostRequest(String _url, List<NameValuePair> params) {
URL url = null;
try {
url = new URL(_url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded; charset= utf-8");
conn.setDoInput(true);
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));
writer.write(getQuery(params));
writer.flush();
writer.close();
os.close();
conn.connect();
conn.getResponseCode();
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
// do something on reader
reader.close();
// 断开连接
conn.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private String getQuery(List<NameValuePair> params) throws UnsupportedEncodingException {
StringBuilder result = new StringBuilder();
boolean first = true;
for (NameValuePair pair : params) {
if (first)
first = false;
else
result.append("&");
result.append(URLEncoder.encode(pair.getName(), "UTF-8"));
result.append("=");
result.append(URLEncoder.encode(pair.getValue(), "UTF-8"));
}
return result.toString();
}
doPostRequst 通过调用setRequestMethod(“POST”) 尝试发送一个post请求,我们post请求是可以包含body的,我们可以讲我们需要传递给服务器的参数放在body部分。
1. setConnectTimeOut指定建立连接的超时时间,如果超过这个时间,就会抛出SocketTimeOutException,如果我们没有指定,那么默认值是0,但是不代表永远不超时,在数分钟之后仍然会抛出TCP超时
2. setReadTimeOut是指定数据读取的超时时间
3. setRequestProperty,设置请求的属性,在这个函数里面设置的属性,就是配置了Http请求头中的属性, 比如上面例子中,我们配置了Content-type属性
4. 通过connection.getOutPutStream拿到输出流,向这个流中写入我们想传得参数,当然在这之前我们必须调用setDoOutput(true)允许connection写入数据,
5. setDoInput 指定connection是否允许接受数据,默认情况下位true,通常不需要设置
如果我们每次,要发送http请求都需要自己构造HttpUrlConnection,确实很繁琐,多余,因为需要自己去设置header和body,部分。如果涉及到请求的取消等操作,那就更繁琐,如果能把这些东西继承起来,包装成一个更易容的接口岂不是更好,没错Volley就是这样一个库,类似的库有很多,比如ion,OkHttp等,我相信大部分人都熟练能够熟练使用volley了,如果还不会,请参考郭霖的文章,他详细讲解了volley的基本用法。今天我要讲的是volley是如何封装httpUrlConnection的。
2.封装
我们先从RequestQueue的创建看起
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
我们注意到在Volley.newRequestQueue时,我们会判断当前系统api版本,如果大于9,则会生成一个HurlStack,否则会生成一个HttpClientStack,其中HurlStack是使用HttpUrlConnection进行http请求的发送,而后者则是使用HttpClient。
我们直接进入到HurlStack,找到performRequest函数,
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
Somehow, 没有缓存的request会走到这一步,大家肯定能一眼就看到HttpUrlConnection, 没错,这里volley对其进行了封装,我们可以看到熟悉的创建URL,openConnection还有addRequestProperty,可并没有setDoInOutput, setRequestMethod等,没关系,都在 setConnectionParametersForRequest这个函数里面,
static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Method.GET:
// Not necessary to set the request method because connection defaults to GET but
// being explicit here.
connection.setRequestMethod("GET");
break;
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
//...other methods
}
这个函数针对不同的http请求方法对connection做了不同的设置,我们主要看Get和Post部分,Get方法,因为参数都在请求url里面,因此不需要添加任何的body,但是post的方法就需要了。我们接着看addBodyIfExists
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
}
这个函数里面我们要读取request的两个属性, 第一个是getBodyContentType,并调用connection的setRequestProperty将这个属性写到请求头里面,第二个是request.getBody,我们会将读取到的body byte数组,写到connection.getOutputStream,跟我们上一章节讲的一样,而且需要首先设置setDoOutput(true)。
但是很多朋友肯定会问,我们在用volley时,写post参数,通常是用一个map
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
/**
* Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
*/
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}
相信大家看到上述代码,肯定豁然开朗,原来参数原先也是从getParams中拿,但是,拿到之后做了进一步的编码,我们看ecodeParameters函数,会发现,从getparams中拿到的每一对参数都是先用getParamsEncoding拿到的编码方式,去进行编码,然后用“=”符号连接起来的。大家肯定有有疑问了,http body的编码方式不应该是从header部分content-type拿到的吗?对,我们来看代码:
protected String getParamsEncoding() {
return DEFAULT_PARAMS_ENCODING;
}
/**
* Returns the content type of the POST or PUT body.
*/
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
Request的getBodyContentType实际上调用了getparamsEncoding,默认的编码方式是utf-8,这两个函数我们在定义自己的Request的时候都是可以去重写的,这样就能定义自己http请求的编码方式和content-type。
Body部分的添加就是如此简单,可能对http更有经验的朋友们会提出疑问了
2.1 发送Json
- 如果我的body部分不是键值对的形式了,我如果是想传一段json或者其他格式的文件过去呢? 或者我是想向服务器上传一个文件过去呢?
如果是第一种情况的话,我们只需要重写getBody,并返回一段代表指定格式数据的byte数组即可,我们以json格式为例:我们只需要将一个JSONObject转为String,并调用String的getBytes即可,其实我说的这个volley已经帮我们做了,请看JsonRequest中的getBody方法
@Override
public byte[] getBody() {
try {
return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, PROTOCOL_CHARSET);
return null;
}
}
这个mRequestBody就是代表json的字符串。需要特别提醒大家注意的是,如果你想继承JsonRequst,就不要再在getParams里面设置post参数了,很明显getBody已经不会再去调用getParams拿参数了,加了也是白加。
2.2 上传文件
- 如果你想上传文件怎么办呢?
对前端很熟悉的朋友们一定知道在用表单上传文件到服务器时,我们需要制定表单的ENCTYPE=”multipart/form-data”,同样,同样我们猜想可以讲content-type设为multipart/form-data,然后将文件流转为为byte数组发送出去,大致是不错的,可是在body部分的处理上还是有很多区别的,比如我们想同时传一个文件和一个字符串过去,就像html表单一样,或者我们想同时上传两个文件,该如何呢?这个时候我们是不是该在body部分有个分隔符把这三者给分开开来,对,我们需要定义一个边界,话说HTTP协议就是这么规定的,感兴趣的朋友们可以详细研究RFC1867,我们只需要知道大概的表现形式,学会如何去用就可以了,我从RFC中copy了一个例子
<FORM ACTION="http://server.dom/cgi/handle"
ENCTYPE="multipart/form-data"
METHOD=POST>
What is your name? <INPUT TYPE=TEXT NAME=submitter>
What files are you sending? <INPUT TYPE=FILE NAME=pics>
</FORM>
这是一段html,话说我怎么开始讲html了,但是事实上互联网时出现的,移动互联网是小弟,说以我们想移动端该如何做,可以先模仿web上的机制,话说回来,刚才那个表单提交时会发出一个post请求,大家可以用fiddler设置代理截取这短内容,请求的格式如下:
Content-type: multipart/form-data, boundary=AaB03x
--AaB03x
content-disposition: form-data; name="field1"
Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
其中header部分包括content-type属性为multipart/form-data, boundary=AaB03x,指定了一个boundary,这个boundary就是用来分割body部分的数据的,对于body部分的数据,我们会对每一块数据都指定一个头header,这样服务器就可以从header知道如何去解码和存储这些数据,对于string类型,我们只需要content-disposition 和name属性即可,对于文件上传,我们还需要指定filename和content-type属性。 header后面是一个空行,请注意这个空行很重要,指示了header部分结束,下面就是真正的data部分,data结束了之后也是一个空行,最后就是分割结束。这样就完整的构造了一个上传文件的post请求,我写了一个用HttpUrlConnection来实现这部分的代码,朋友们可以参考
public static String uploadFile(String RequestURL, File file) {
String BOUNDARY = UUID.randomUUID().toString(); // 边界标识 随机生成
String PREFIX = "--", LINE_END = "rn";
String CONTENT_TYPE = "multipart/form-data"; // 内容类型
try {
URL url = new URL(RequestURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(TIME_OUT);
conn.setConnectTimeout(TIME_OUT);
conn.setDoInput(true); // 允许输入流
conn.setDoOutput(true); // 允许输出流
conn.setUseCaches(false); // 不允许使用缓存
conn.setRequestMethod("POST"); // 请求方式
conn.setRequestProperty("Charset", CHARSET); // 设置编码
conn.setRequestProperty("connection", "keep-alive");
conn.setRequestProperty("Content-Type", CONTENT_TYPE + ";boundary="
+ BOUNDARY);
if (file != null) {
/**
* 当文件不为空,把文件包装并且上传
*/
OutputStream outputSteam = conn.getOutputStream();
DataOutputStream dos = new DataOutputStream(outputSteam);
StringBuffer sb = new StringBuffer();
sb.append(PREFIX);
sb.append(BOUNDARY);
sb.append(LINE_END);
/**
* 这里重点注意: name里面的值为服务器端需要key 只有这个key 才可以得到对应的文件
* filename是文件的名字,包含后缀名的 比如:abc.png
*/
sb.append("Content-Disposition: form-data; name="img"; filename=""
+ file.getName() + """ + LINE_END);
sb.append("Content-Type: application/octet-stream; charset="
+ CHARSET + LINE_END);
sb.append(LINE_END);
dos.write(sb.toString().getBytes());
InputStream is = new FileInputStream(file);
byte[] bytes = new byte[1024];
int len = 0;
while ((len = is.read(bytes)) != -1) {
dos.write(bytes, 0, len);
}
is.close();
dos.write(LINE_END.getBytes());
byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINE_END)
.getBytes();
dos.write(end_data);
dos.flush();
/**
* 获取响应码 200=成功 当响应成功,获取响应的流
*/
int res = conn.getResponseCode();
if (res == 200) {
return SUCCESS;
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return FAILURE;
}
}
其实说白了,就是按照Http协议规定的格式,填充好body部分的数据,设置好content-type就可以发送了。话说,如果把这个封装在volley就是一个上传文件的Request,嘿嘿。不过上面的代码有很多不严谨的地方,比如没有考虑字符串和文件同时存在的情况,也没有考虑同时上传多个文件的情况。幸好,这些代码不用自己写,我们可以利用现有的库HttpClient中的MultipartEntityBuilder帮我们达成。那废话我就不多说了,直接上一块代码:
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyLog;
import org.apache.http.HttpEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Junli on 3/23/15.
*/
public class MultipartRequest extends Request<String> {
private HttpEntity mEntity;
private static final String FILE_PART_NAME = "file";
private static final String STRING_PART_NAME = "text";
private final Response.Listener<String> mListener;
private List<File> mFileParts = new ArrayList<>();
private List<String> mStringParts = new ArrayList<>();
public MultipartRequest(String url, List<File> files, List<String> stringPart,
Response.ErrorListener errorListener, Response.Listener<String> listener )
{
super(Request.Method.POST, url, errorListener);
mListener = listener;
mFileParts = files;
mStringParts = stringPart;
buildMultipartEntity();
}
private void buildMultipartEntity()
{
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
int i = 0;
for(File file : mFileParts)
{
builder.addPart(FILE_PART_NAME + i, new FileBody(file));
i++;
}
i = 0;
for(String str : mStringParts)
{
try {
builder.addPart(STRING_PART_NAME+i, new StringBody(str));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
i++;
}
mEntity = builder.build();
}
@Override
public String getBodyContentType()
{
return mEntity.getContentType().getValue();
}
@Override
public byte[] getBody() throws AuthFailureError
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
mEntity.writeTo(bos);
} catch (IOException e) {
VolleyLog.e("IOException writing to ByteArrayOutputStream");
}
return bos.toByteArray();
}
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response)
{
return Response.success("Uploaded", getCacheEntry());
}
@Override
protected void deliverResponse(String response)
{
mListener.onResponse(response);
}
}
这样,我们就自定义了一个multipart/form-data类型的Request类型,可是,上传文件这么重要的操作,为什么volley不帮我们做好呢?我们看源码Request里面直接默认的content-type类型就是application/x-www-form-urlencoded,似乎Google不建议我们用volley上传文件? 正是如此,在此我引用stackoverflow上一位朋友的陈述:
As mentioned in the presentation at the I/O (about 4:05), Volley “is terrible” for large payloads. As I understand it that means not to use Volley for receiving/sending (big) files. Looking at the code it seems that it is not even designed to handle multipart form data (e.g. Request.java has getBodyContentType() with hardcoded “application/x-www-form-urlencoded”; HttpClientStack::createHttpRequest() can handle only byte[], etc…). Probably you will be able to create implementation that can handle multipart but If I were you I will just use HttpClient directly with MultipartEntity。
他说Google不建议我们使用volley建议大文件的发送和接受,当然我们可以自己定义这样的实现,但是效率会很差。建议使用HttpClient加MultipartEntity实现大文件的发送和接受,像这样的代码:
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost uploadFile = new HttpPost("...");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("field1", "yes", ContentType.TEXT_PLAIN);
builder.addBinaryBody("file", new File("..."), ContentType.APPLICATION_OCTET_STREAM, "file.ext");
HttpEntity multipart = builder.build();
uploadFile.setEntity(multipart);
response = httpClient.execute(uploadFile);
responseEntity = response.getEntity();
好了,对于文件的上传,我们讲了很久了,对于这部分不敢兴趣的朋友,实在对不住了,马上换题目。
2.3 返回response
如果不出意外,我们大部分的Request都能发送出了,现在就等着返回了,从哪里返回呢?刚才我们讲HttpUrlConnection时谈到,直到调用connection.getInputStream才会发送真正的http请求,获得response,我们看HurlStack是怎么做的。
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream);
entity.setContentLength(connection.getContentLength());
entity.setContentEncoding(connection.getContentEncoding());
entity.setContentType(connection.getContentType());
return entity;
}
从代码中,我们看到从调用connection.getResponseCode开始,我们陆续拿到了http相应的返回码,其他还有
1. getResponseMessage 比如 OK, NotFound等
2. getInputStream拿到body部分的数据content,
3. getContentLength拿到Content-length
4. getContentEncoding getContentType拿到content的编码方式和类型
5. getHeaderFields返回一个map
2.4 解码数据
BasicNetwork返回的response为NetworkResponse, NetworkResponse与HttpResponse类似,唯一的区别就是httpresposne中的entity为stream,而NetworkResponse,将从这个stream里面读取数据,转为Byte数组。读取到这个byte数组之后,我们需要进一步将其转为用户可以直接使用的类型,这一步需要调用
Response<?> response = request.parseNetworkResponse(networkResponse);
至于怎么转化,每个不同的类型有转码方式,基类Request肯定不知道怎么做,需要子类去实现,因此这个方法是个abstract函数,子类怎么实现,取决于它需要拿取的数据,我们来看一个简单地例子,比如StringRequest
public class StringRequest extends Request<String> {
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
}
StringRequest继承自Request,请注意泛型类Request中的T指的就是我们需要从response中获取的数据类型。对于StringRequest当然就是String, parse的过程也很简单,首先去header部分拿到charset,然后用这个charset去解码。拿到解码后的String之后,再用静态方法success构建出一个Response出来,Response的构造函数第二个参数是一个CacheEntry,这个是用来做缓存的。
Volley里面帮我们实现了一个类JsonRequest,这个Request 会在http请求中发送json数据,至于Response的数据类型,取决于T的类型,我们可以根据T的类型采取不同的parse方法,如果我们既需要发json也需要接受json,可以用它的子类JsonObjectRequest
public class JsonObjectRequest extends JsonRequest<JSONObject>{
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
}
JsonObjectRequest会利用Response的数组先解码为string,然后用这个string直接构造出一个JSONObject。
拿到数据之后,最后一步是mDelivery.postResponse(request, response); 这个mDelivery是一个ResponseDelivery类,默认情况下volley会传一个ExecutorDelivery类,这个类使用一个Executor来执行我们
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
这个类,会将我们需要执行的任务以Runnable的形式post到主线程上去,那我们postResponse做了什么呢?
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
private class ResponseDeliveryRunnable implements Runnable {
@SuppressWarnings("unchecked")
@Override
public void run() {
//....
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
//....
}
因此看这个代码,我们就知道了最后我们调的就是Request类中得deliverResponse函数,这个函数也是个abstract函数,需要我们子类自己去实现,大部分的实现都是去调用ResponseLisnter,通知用户,相应返回了。
从发送到返回,相信大家对Request的工作原理也明白了不少,我来整理下大家在自定义自己的Request的需要做的事情:
1. getBody与getParams 都是用来设置请求体的,如果是键值对,直接在getParams里设置就可以了,如果是其他数据类型,我们可以重写getBody将数据编码成byte数组返回
2. getBodyContentType 设置header中content-type属性,这个也可以在getheader中设置
3. 如果有其它属性需要设置,比如关于cache和use-agent就重写getHeader
4. 重写parseNetworkRespse,在这里将返回的byte数组转成我们需要的类型
5. 重写deliverResponse,调用ResponseListener或者对返回数据做更多的自定义操作
相信大家看到这里也就累了,不管怎么样希望能给朋友们带来收获吧,先写到这里,这只是上篇,还有下篇,朋友们不要走太远哦^_^
最后
以上就是安静草莓为你收集整理的浅谈Android中Http请求与缓存(上)1. HttpUrlConnection2.封装的全部内容,希望文章能够帮你解决浅谈Android中Http请求与缓存(上)1. HttpUrlConnection2.封装所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复