Android 学习笔记之Volley开源框架解析(五)
学习内容:
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开源框架解析(五)的更多相关文章
- Android 学习笔记之Volley开源框架解析(一)
		
PS:看完了LGD的六场比赛...让人心酸... 学习内容: 1.Http请求的过程... 2.Volley的简单介绍... 1.Http请求... 这里只是简单的说一下Http请求的过程.. ...
 - Android 学习笔记之Volley开源框架解析(四)
		
学习内容: 1.NetWorkDispatcher网络请求线程调度... 2.NetWork网络请求抽象类... 3.BasicNetWork网络请求抽象类的具体实现... 4.NetWorkResp ...
 - Android 学习笔记之Volley开源框架解析(二)
		
PS:Volley已经学完,可以安心的写一下博客总结一下了... 学习内容: 1.Request的完整封装... 2.RetryPolicy,DefaultRetryPolicy(请求重试策略源码解析 ...
 - Android 学习笔记之Volley开源框架解析(三)
		
学习内容: 1.CacheDispatcher缓存请求调度... 2.Cache缓存数据的保存... 3.DiskBasedCache基于磁盘的缓存类实现方式... 前面说到使用Volley发 ...
 - Android 学习笔记之Volley(七)实现Json数据加载和解析...
		
学习内容: 1.使用Volley实现异步加载Json数据... Volley的第二大请求就是通过发送请求异步实现Json数据信息的加载,加载Json数据有两种方式,一种是通过获取Json对象,然后 ...
 - Android(java)学习笔记214:开源框架的文件上传(只能使用Post)
		
1.文件上传给服务器,服务器端必然要写代码进行支持,如下: 我们新建一个FileUpload.jsp的动态网页,同时我们上传文件只能使用post方式(不可能将上传数据拼凑在url路径下),上传数据Ap ...
 - Android(java)学习笔记157:开源框架的文件上传(只能使用Post)
		
1.文件上传给服务器,服务器端必然要写代码进行支持,如下: 我们新建一个FileUpload.jsp的动态网页,同时我们上传文件只能使用post方式(不可能将上传数据拼凑在url路径下),上传数据Ap ...
 - Android(java)学习笔记213:开源框架post和get方式提交数据(qq登录案例)
		
1.前面提到Http的get/post方式 . HttpClient方式,实际工作的时候不常用到,因为这些方式编写代码是很麻烦的 2.Android应用会经常使用http协议进行传输,网上会有很完善 ...
 - Android(java)学习笔记156:开源框架post和get方式提交数据(qq登录案例)
		
1. 前面提到Http的get/post方式 . HttpClient方式,实际工作的时候不常用到,因为这些方式编写代码是很麻烦的 2. Android应用会经常使用http协议进行传输,网上会有很 ...
 
随机推荐
- C#与C++之间类型的对应
			
Windows Data Type .NET Data Type BOOL, BOOLEAN Boolean or Int32 BSTR String BYTE Byte CHAR Char DOUB ...
 - Spring 4.3.2下实现http多次断点下载
			
其实跟 spring 无关,如果是直接下载资源很多 web sever 不用程序就直接实现了断点. 但我们的应用是 download?url=xxxx 这种方式 下载资源由 download 来负责, ...
 - Selenium3笔记-WebDriver源码初探
			
Selenium3 有哪些变化? 其实相对于与Selenium2,Selenium3没有做太多的改动.下面给出官方的文档说明,供参考. 参考文档:https://seleniumhq.wordpres ...
 - C#中将结构类型数据存储到二进制文件中方法
			
以往在vb6,vc6中都有现成的方法将结构类型数据写入和读取到二进制文件中,但是在c#中却没有现成的方法来实现,因此我查阅了一些资料,借鉴了网上一些同学的做法,自己写了个类似的例子来读写结构类型数据到 ...
 - MySQL的慢查询分析
			
慢查询分析日最初是用来捕获比较“慢”的查询,在mysql5.1 + 版本中,慢查询的功能被加强,可以通过设置long_query_time为0来捕获所有的查询,而且查询的响应时间已经可以做到微妙级别. ...
 - Chrome谷歌浏览器首页被改为Hao123导航怎么办|附各类解决方法【转】
			
软件小子:昨天偶然间发现自己的chrome浏览器的首页被篡改成hao123导航了,要是自己设置的还无所谓,但是后面还有尾巴.顿时就火了,又是哪款软件这么流氓,太无良了,我非常确定我肯定是没有勾选什么设 ...
 - ios开发中的Swift面向对象
			
iOS在现代计算机语言中,面向对象是非常重要的特性,Swift语言也提供了面向对象的支持.而且在Swift语言中,不仅类具有面向对象特性,结构体和枚举也都具有面向对象特性. 1.Swift中的类和结构 ...
 - asp.net core 之静态文件目录的操作
			
文章前言 之前写了一篇关于模拟登录的文章,自我感觉内容不太丰富,今天的这篇文章,希望在内容上能丰富些.本人缺少写文章的经验,技术上也是新手,但我会努力的,希望大家多多支持小弟. asp.net cor ...
 - 笔记 - 本地拦截genymotion或者Android模拟器的网络请求
			
我们在主机上面运行了Burp或者fiddler,那么代理已经监听在本机的8080端口了. 那么我们需要在模拟器中进行如下设置: 1.在设置中,长按当前连接的wifi网络,弹出如下: 2. 点击修改网络 ...
 - 文件系统管理 之 实例解说 fdisk 使用方法
			
一.fdisk 的介绍: fdisk - Partition table manipulator for Linux ,译成中文的意思是磁盘分区表操作工具:本人译的不太好,也没有看中文文档:其实就是分 ...