码迷,mamicode.com
首页 > 移动开发 > 详细

Android 学习笔记之Volley开源框架解析(五)

时间:2015-10-24 23:17:37      阅读:281      评论:0      收藏:0      [点我收藏+]

标签:

 

 

学习内容:

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开源框架解析(五)

标签:

原文地址:http://www.cnblogs.com/RGogoing/p/4907488.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!