学习内容:

1.PoolingByteArrayOutputStream

2.ByteArrayPool

3.HttpStack

4.HurlStack

5.HttpHeaderParser

  前面整体的解释了网络请求——响应的整个过程,只是其中还是涉及到了一些其他的类,因此在这里都说一说,最后几篇会是Volley正式做一些请求,这篇仍然是进行源码解析...

1.PoolingByteArrayOutputStream.java

  PoolingByteArrayOutputStream继承与ByteArrayOutputStream...当ByteArrayOutputStream进行写入数据操作时,需要通过缓冲buf来作为缓冲机制,如果缓存的空间不足,那么需要new一个更大数量级的buf来作为缓冲机制,这样会增加内存的分配个释放的过程,而PoolingByteArrayOutputStream的优势在于它内部实现了回收机制,可以对Byte进行回收和再次利用,减少了频繁分配内存和释放的操作..

  Byte回收池机制通过使用ArrayList,一共有两个ArrayList决定了,一个用于从小到大的顺序保存Byte[]...另一个按照时间顺序,用于缓存一旦满了时,按照时间顺序进行清除..

package com.android.volley.toolbox;

import java.io.ByteArrayOutputStream;
import java.io.IOException; public class PoolingByteArrayOutputStream extends ByteArrayOutputStream { private static final int DEFAULT_SIZE = 256; //定义缓冲池的默认字节大小.. private final ByteArrayPool mPool; //定义比特回收池对象... //按照默认的方式去构造一个缓冲池对象...
public PoolingByteArrayOutputStream(ByteArrayPool pool) {
this(pool, DEFAULT_SIZE);
} //按照人为指定的大小去构造一个缓冲池对象...
public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) {
mPool = pool;
buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE));
}
//释放内存...关闭流的操作...
@Override
public void close() throws IOException {
mPool.returnBuf(buf);
buf = null;
super.close();
}
//返回一个缓冲池...
@Override
public void finalize() {
mPool.returnBuf(buf);
} //写入数据前需要调用的函数,确保在缓存有足够大的空间来满足给定的字节大小...
private void expand(int i) {
/* Can the buffer handle @i more bytes, if not expand it */
if (count + i <= buf.length) {
return;
}
byte[] newbuf = mPool.getBuf((count + i) * 2);
System.arraycopy(buf, 0, newbuf, 0, count);
mPool.returnBuf(buf);
buf = newbuf;
}
//写入数据函数...
@Override
public synchronized void write(byte[] buffer, int offset, int len) {
expand(len);
super.write(buffer, offset, len);
} @Override
public synchronized void write(int oneByte) {
expand(1);
super.write(oneByte);
}
}

  PoolingByteArrayOutputStream只是对流的一个封装,内部实现了写操作的实现方法...它基于缓冲回收机制,那么缓冲回收机制的实现类也必然是需要我们去清楚的...需要明确是如何实现的缓冲回收...

2.ByteArrayPool.java

  实现回收机制的实现类...通过使用两个ArrayList实现了缓冲池回收机制,一个是按照大小顺序来保存使用过的Byte[]缓冲,另一个则是按照时间顺序对Byte[]进行保存...

package com.android.volley.toolbox;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List; public class ByteArrayPool { private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();//以时间的先后顺序保存Byte[]
private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);//以字节大小的顺序保存Byte[] /** The total size of the buffers in the pool */
private int mCurrentSize = 0; //记录Bytep[]的数量... private final int mSizeLimit; //大小限制.. /** Compares buffers by size */
//对所有的Byte[]进行大小比较...
protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() {
@Override
public int compare(byte[] lhs, byte[] rhs) {
return lhs.length - rhs.length;
}
}; /**
* @param sizeLimit the maximum size of the pool, in bytes
*/
//设置允许的最大Byte[]...
public ByteArrayPool(int sizeLimit) {
mSizeLimit = sizeLimit;
} //获取缓冲的过程,如果缓冲池当中存在一个合适的缓冲区,那么就return这个缓冲区,如果没有合适的,那么就需要新建立一个缓冲区...
public synchronized byte[] getBuf(int len) {
for (int i = 0; i < mBuffersBySize.size(); i++) { //遍历过程...
byte[] buf = mBuffersBySize.get(i); //获取每一个缓冲..
if (buf.length >= len) { //如果满足规格...
mCurrentSize -= buf.length;
//表示缓冲被占用,那么ArrayList就将其移除并返回...
mBuffersBySize.remove(i);
mBuffersByLastUse.remove(buf);
return buf; //返回合适的buf...
}
}
return new byte[len]; //如果没有,新建立一个缓冲...
}
//对使用过的Byte[]进行保存...根据大小插入到合适的ArrayList中...
public synchronized void returnBuf(byte[] buf) {
if (buf == null || buf.length > mSizeLimit) {
return;
}
mBuffersByLastUse.add(buf); //加入这次使用过的缓冲...
//比较完大小进行插入..
int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
if (pos < 0) {
pos = -pos - 1;
}
mBuffersBySize.add(pos, buf);//插入过程...
mCurrentSize += buf.length; //记录当前大小...
trim(); //trim()函数调用...
} //函数的目的是判断,如果使用过的缓冲大小超出了预先设定的大小,那么按照先进先出的原则,缓冲会移除每次都移除第一个Byte,当Byte[]满足了指定大小,就不用再删除字节了...
private synchronized void trim() {
while (mCurrentSize > mSizeLimit) {
byte[] buf = mBuffersByLastUse.remove(0);
mBuffersBySize.remove(buf);
mCurrentSize -= buf.length;
}
} }

3.HttpStack.java

  前面涉及到的网络请求都是通过Http协议来完成请求的,在new Request的时候需要建立一个栈区来保存所有请求,那么这个栈区则是通过HttpStack来实现的,而HttpStack只是一个抽象类的接口...

package com.android.volley.toolbox;

import com.android.volley.AuthFailureError;
import com.android.volley.Request; import org.apache.http.HttpResponse; import java.io.IOException;
import java.util.Map;
public interface HttpStack { public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError; }

  我们知道Http请求一种是通过HttpURLConnection通过url来建立一个连接的,而另一种方式则是通过HttpClient,基于Apache的一种请求方式,而Android从2.3版本以后就推荐使用第一种连接方式来创建一个连接...因此我们就说一下HttpUrlConnectionStack,在Volley中实现类以HurlStack,创建一个url连接的栈区,来保存所有通过url来建立的网络连接...

4.HurlStack.java

package com.android.volley.toolbox;

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Request.Method; import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine; import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory; public class HurlStack implements HttpStack { private static final String HEADER_CONTENT_TYPE = "Content-Type"; //Header内容的类型... public interface UrlRewriter {
public String rewriteUrl(String originalUrl); //重写一个url...
} private final UrlRewriter mUrlRewriter; //url重写对象...
private final SSLSocketFactory mSslSocketFactory; //用于Https请求... public HurlStack() {
this(null);
} public HurlStack(UrlRewriter urlRewriter) {
this(urlRewriter, null);
} //创建一个栈区,保存url连接...
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
} @Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) //执行请求的过程...
throws IOException, AuthFailureError {
//获取url...
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
//为请求加上头部,以及我们传递的额外参数..
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
//对url进行重写,重写的好处使url更加的保密,安全...
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
//创建url对象...
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,抛出异常...
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)); //获取响应中的实体...
//对Header进行遍历...为响应添加相关的Header...
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(); //获取连接的I/O流..
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream); //实体的内容..
entity.setContentLength(connection.getContentLength());//实体的长度..
entity.setContentEncoding(connection.getContentEncoding()); //实体的编码...
entity.setContentType(connection.getContentType());//实体内容的类型...
return entity;
}
//创建连接...
protected HttpURLConnection createConnection(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
//打开连接方法,,,
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
HttpURLConnection connection = createConnection(url); int timeoutMs = request.getTimeoutMs(); //设置请求的超时时间..
connection.setConnectTimeout(timeoutMs); //连接超时时间的设置..
connection.setReadTimeout(timeoutMs); //读取时间超时...
connection.setUseCaches(false); //是否设置缓存..
connection.setDoInput(true); //是否有相关的输入.. // use caller-provided custom SslSocketFactory, if any, for HTTPS
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory); //创建一个Https连接...
} return connection;
}
//为请求设置连接方式...
@SuppressWarnings("deprecation")
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST:
byte[] postBody = request.getPostBody();
if (postBody != null) {
// Prepare output. There is no need to set Content-Length explicitly,
// since this is handled by HttpURLConnection using the size of the prepared
// output stream.
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty(HEADER_CONTENT_TYPE,
request.getPostBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(postBody);
out.close();
}
break;
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.DELETE:
connection.setRequestMethod("DELETE");
break;
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request); //调用最后一个函数...
break;
case Method.PUT:
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request);
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}
//如果一个请求存在实体数据,那么需要为其加上请求数据...
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();
}
}
}

 前面说到一个请求数据是否需要缓存是通过Header中封装的数据来判断一次请求后的数据是否需要进行缓存...那么是否需要缓存,以及新鲜度的验证等等头被封装在了Header中,那么我们是如何知道Header中的数据呢?前面已经可以获取到响应数据报中的实体部分(Body),那么Header还没有被获取..因此需要说一下HttpHeaderParser类...用来解析Header中的数据...

5.HttpHeaderParser.java

package com.android.volley.toolbox;

import com.android.volley.Cache;
import com.android.volley.NetworkResponse; import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP; import java.util.Map;
public class HttpHeaderParser { //解析缓存Header...
public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis(); Map<String, String> headers = response.headers;
//一些基本数据的定义...
long serverDate = 0;
long serverExpires = 0;
long softExpire = 0;
long maxAge = 0;
boolean hasCacheControl = false; String serverEtag = null;
String headerValue;
//获取请求——服务的整个时间,将RFC1123的格式解析成epoch方式...
headerValue = headers.get("Date");
if (headerValue != null) {
serverDate = parseDateAsEpoch(headerValue);
} headerValue = headers.get("Cache-Control");
if (headerValue != null) {
hasCacheControl = true;
String[] tokens = headerValue.split(",");
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i].trim(); //判断是否有缓存...
if (token.equals("no-cache") || token.equals("no-store")) {
return null;
} else if (token.startsWith("max-age=")) {
try {
maxAge = Long.parseLong(token.substring(8)); //设置缓存的有效时间...
} catch (Exception e) {
}
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
maxAge = 0; //如果不允许缓存,那么有效期为0...
}
}
} headerValue = headers.get("Expires");
if (headerValue != null) {
serverExpires = parseDateAsEpoch(headerValue); //设置缓存新鲜度时间...
} serverEtag = headers.get("ETag"); //设置缓存的过期时间...
if (hasCacheControl) {
softExpire = now + maxAge * 1000;
} else if (serverDate > 0 && serverExpires >= serverDate) {
// Default semantic for Expire header in HTTP specification is softExpire.
softExpire = now + (serverExpires - serverDate);
}
//将Header数据保存在Entry当中..
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = entry.softTtl;
entry.serverDate = serverDate;
entry.responseHeaders = headers; return entry;
} //将RFC1123的时间格式转换成epoch格式...
public static long parseDateAsEpoch(String dateStr) {
try {
// Parse date in RFC1123 format if this header contains one
return DateUtils.parseDate(dateStr).getTime();
} catch (DateParseException e) {
// Date in invalid format, fallback to 0
return 0;
}
} //解析字符集...
public static String parseCharset(Map<String, String> headers) {
String contentType = headers.get(HTTP.CONTENT_TYPE);
if (contentType != null) {
String[] params = contentType.split(";");
for (int i = 1; i < params.length; i++) {
String[] pair = params[i].trim().split("=");
if (pair.length == 2) {
if (pair[0].equals("charset")) {
return pair[1];
}
}
}
} return HTTP.DEFAULT_CONTENT_CHARSET;
}
}

 Volley中还有一些其他类,不过基本都是一些简单的类,就不粘贴代码进行解析了,只是提一嘴就一笔带过就行了...

 Volley.java:工具类,用于实现一个请求队列...

 Authenticator.java:一个抽象接口,用于身份验证...用于基本认证和摘要认证...不过使用的不是非常的广泛.. 

 AndroidAuthenticator.java:基于Android AccountManager的认证交互类...实现了验证接口的抽象方法...

 VolleyLog.java:在Volley中用于显示Log信息...

 VolleyError.java:Volley内部所有异常类的父类...对异常的处理方式的一个超类...继承了Expection...

 TimeoutError.java

 ServerError.java

 NetWorkError.java

 ParseError.java

 NoConnection.java

 AuthFailureError.java
    都是异常发生如何处理的类,其中包括超时,服务端错误,网络错误,内容解析错误,无法连接错误,验证失败等异常处理...在这里就不一一介绍了..都比较简单...

Android 学习笔记之Volley开源框架解析(五)的更多相关文章

  1. Android 学习笔记之Volley开源框架解析(一)

    PS:看完了LGD的六场比赛...让人心酸... 学习内容: 1.Http请求的过程... 2.Volley的简单介绍...   1.Http请求...   这里只是简单的说一下Http请求的过程.. ...

  2. Android 学习笔记之Volley开源框架解析(四)

    学习内容: 1.NetWorkDispatcher网络请求线程调度... 2.NetWork网络请求抽象类... 3.BasicNetWork网络请求抽象类的具体实现... 4.NetWorkResp ...

  3. Android 学习笔记之Volley开源框架解析(二)

    PS:Volley已经学完,可以安心的写一下博客总结一下了... 学习内容: 1.Request的完整封装... 2.RetryPolicy,DefaultRetryPolicy(请求重试策略源码解析 ...

  4. Android 学习笔记之Volley开源框架解析(三)

      学习内容: 1.CacheDispatcher缓存请求调度... 2.Cache缓存数据的保存... 3.DiskBasedCache基于磁盘的缓存类实现方式...   前面说到使用Volley发 ...

  5. Android 学习笔记之Volley(七)实现Json数据加载和解析...

    学习内容: 1.使用Volley实现异步加载Json数据...   Volley的第二大请求就是通过发送请求异步实现Json数据信息的加载,加载Json数据有两种方式,一种是通过获取Json对象,然后 ...

  6. Android(java)学习笔记214:开源框架的文件上传(只能使用Post)

    1.文件上传给服务器,服务器端必然要写代码进行支持,如下: 我们新建一个FileUpload.jsp的动态网页,同时我们上传文件只能使用post方式(不可能将上传数据拼凑在url路径下),上传数据Ap ...

  7. Android(java)学习笔记157:开源框架的文件上传(只能使用Post)

    1.文件上传给服务器,服务器端必然要写代码进行支持,如下: 我们新建一个FileUpload.jsp的动态网页,同时我们上传文件只能使用post方式(不可能将上传数据拼凑在url路径下),上传数据Ap ...

  8. Android(java)学习笔记213:开源框架post和get方式提交数据(qq登录案例)

    1.前面提到Http的get/post方式  . HttpClient方式,实际工作的时候不常用到,因为这些方式编写代码是很麻烦的 2.Android应用会经常使用http协议进行传输,网上会有很完善 ...

  9. Android(java)学习笔记156:开源框架post和get方式提交数据(qq登录案例)

    1. 前面提到Http的get/post方式  . HttpClient方式,实际工作的时候不常用到,因为这些方式编写代码是很麻烦的 2. Android应用会经常使用http协议进行传输,网上会有很 ...

随机推荐

  1. C#与C++之间类型的对应

    Windows Data Type .NET Data Type BOOL, BOOLEAN Boolean or Int32 BSTR String BYTE Byte CHAR Char DOUB ...

  2. Spring 4.3.2下实现http多次断点下载

    其实跟 spring 无关,如果是直接下载资源很多 web sever 不用程序就直接实现了断点. 但我们的应用是 download?url=xxxx 这种方式 下载资源由 download 来负责, ...

  3. Selenium3笔记-WebDriver源码初探

    Selenium3 有哪些变化? 其实相对于与Selenium2,Selenium3没有做太多的改动.下面给出官方的文档说明,供参考. 参考文档:https://seleniumhq.wordpres ...

  4. C#中将结构类型数据存储到二进制文件中方法

    以往在vb6,vc6中都有现成的方法将结构类型数据写入和读取到二进制文件中,但是在c#中却没有现成的方法来实现,因此我查阅了一些资料,借鉴了网上一些同学的做法,自己写了个类似的例子来读写结构类型数据到 ...

  5. MySQL的慢查询分析

    慢查询分析日最初是用来捕获比较“慢”的查询,在mysql5.1 + 版本中,慢查询的功能被加强,可以通过设置long_query_time为0来捕获所有的查询,而且查询的响应时间已经可以做到微妙级别. ...

  6. Chrome谷歌浏览器首页被改为Hao123导航怎么办|附各类解决方法【转】

    软件小子:昨天偶然间发现自己的chrome浏览器的首页被篡改成hao123导航了,要是自己设置的还无所谓,但是后面还有尾巴.顿时就火了,又是哪款软件这么流氓,太无良了,我非常确定我肯定是没有勾选什么设 ...

  7. ios开发中的Swift面向对象

    iOS在现代计算机语言中,面向对象是非常重要的特性,Swift语言也提供了面向对象的支持.而且在Swift语言中,不仅类具有面向对象特性,结构体和枚举也都具有面向对象特性. 1.Swift中的类和结构 ...

  8. asp.net core 之静态文件目录的操作

    文章前言 之前写了一篇关于模拟登录的文章,自我感觉内容不太丰富,今天的这篇文章,希望在内容上能丰富些.本人缺少写文章的经验,技术上也是新手,但我会努力的,希望大家多多支持小弟. asp.net cor ...

  9. 笔记 - 本地拦截genymotion或者Android模拟器的网络请求

    我们在主机上面运行了Burp或者fiddler,那么代理已经监听在本机的8080端口了. 那么我们需要在模拟器中进行如下设置: 1.在设置中,长按当前连接的wifi网络,弹出如下: 2. 点击修改网络 ...

  10. 文件系统管理 之 实例解说 fdisk 使用方法

    一.fdisk 的介绍: fdisk - Partition table manipulator for Linux ,译成中文的意思是磁盘分区表操作工具:本人译的不太好,也没有看中文文档:其实就是分 ...