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