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协议进行传输,网上会有很 ...
随机推荐
- HBase + Kerberos 配置示例(二)
接上篇<HBase + Kerberos配置示例(一)>,我们继续剩下的配置工作. 环境准备 Hadoop配置 Zookeeper配置 HBase配置 Java测试程序 环境准备 安装ha ...
- 查看Exchange邮件队列(queue)
#加载Exchange管理模块 Add-PSSnapin Microsoft.Exchange.Management.PowerShell.snapin get-queue #查看队列 get-mes ...
- IOC性能对比
- 使用 dbms_xplan.display 按照 plan_hash_value 查执行计划的方法
dbms_xplan.display_* 能按照 plan_hash_value 只有 display_awr 方法,如果这个SQL PLAN 刚刚生成,没有写入到AWR怎么办呢? 可以将 V$SQL ...
- 使用commons-beanutils迭代获取javabean的属性
NoteEntity entity = new NoteEntity(); entity.setNote001("a1"); entity.setNote002("a2& ...
- 64位Windows无法打开会声会影X5的解决方法
低版本和破解版都不好用了. 由于微软更新补丁而引起的会声会影停止工作的问题,建议大家卸载补丁[KB3126587]和[KB3126593][KB2140410 ]解决或者(修复包)修复,更换高版本使用 ...
- SQL Server死锁
SQL Server死锁 多个事务之间互相等待对方的资源,导致这些事务永久等待 注意是永久等待,而非长事务 死锁的4个条件 互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程 ...
- 菜鸟学JS(四)——javascript为按钮注册回车事件(设置默认按钮)
不得不说,在JS方面,自己真的是个不折不扣的菜鸟.对于JS以及一些JS框架如JQuery等JS框架,自己也只是处在简单应用的阶段,当然自己也在不断的学习当中,希望将来能跟大家分享更多JS方面的心得.今 ...
- algo_预备
章 C + +程序设计 大家好!现在我们将要开始一个穿越" 数据结构.算法和程序" 这个抽象世界的特殊旅程,以解决现实生活中的许多难题.在程序开发过程中通常需要做到如下两点:一是高 ...
- [原]OpenGL基础教程(四)VBO+纹理绘制四边形
工程下载地址:http://pan.baidu.com/s/1ntr7NHv 提取码:yf1h 一.本文牵扯知识点梳理: (1)VBO (2)纹理 (3)libpng(加载png) (4)shader ...