Volley尝试源码解析1

来源:互联网 发布:网络划分的策略 编辑:程序博客网 时间:2024/06/09 21:06

Volley源码分析一

这是做Android开发以来第一次分析网络框架,虽然项目做了不少,但是之前都是在公司项目封装好的基础框架上做开发,网络架构也早已经被公司大牛封装好了,所以请求网络对于我们这种拿来主义的人来说,就是简单的调用方法,穿几个参数,写几个bean,但是but这样时间长了真的好吗?本人得出的答案是不好,所以打算写一个Volley框架源码解析的系列。或许就有人说了,网上Volley源码解析一搜一大堆,还有必要再造一次轮子吗,这不是吃力不讨好,但是本人的目的不在于有没有人看,而是对自己的一个要求。本系列就是拿出其中一些类一个一个的分析,期待最终会有多大的收获。

  1. RequestQueue

这个用过Volley框架的人都知道,这里第一个分析它,应该不用过多解释吧,好了,Volley三部曲:第一曲,Volley.newRequestQueue(Context context)得到RequestQueue(我把这个类比作是一个黑盒子,有request请求就把它丢到这个黑盒子里,但是这个黑盒子很神奇(由于水平有限,很多东西不能分析的非常到位,但是作为初级水平来说,如果看源码或者框架如果一下子看的非常细,往往是有难度的,所以这个时候如果故意的模糊一些东西,比如这里被称为黑盒子的东西,把一个大的东西的流程或者主线看懂,再慢慢的攻克这些黑盒子,最终一定会有惊喜),经过它之后,我们就能得到想要的数据。先贴出源码

public class Volley {    /** Default on-disk cache directory. */    //默认的缓存路径    private static final String DEFAULT_CACHE_DIR = "volley";    /**     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.     * 创建一个默认的线程池实力并且调用start()方法     * You may set a maximum size of the disk cache in bytes.     *或许你应该设置一个最大的缓存区大小     * @param context A {@link Context} to use for creating the cache dir.     * 上下文环境,用于创建缓存目录     * @param stack An {@link HttpStack} to use for the network, or null for default.     * HttpStack对象用来执行网络请求操作,默认可以为空     * @param maxDiskCacheBytes the maximum size of the disk cache, in bytes. Use -1 for default size.     * 缓存区大小,传入-1代表使用默认大小     * @return A started {@link RequestQueue} instance.     * 返回RequestQueue对象     */    public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);//创建缓存区目录        String userAgent = "volley/0";  //网络的一个请求头(就知道这么多),一般设置为应用的packageName + "/"+versionCode        try {            String packageName = context.getPackageName();            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);            userAgent = packageName + "/" + info.versionCode;        } catch (NameNotFoundException e) {        }        if (stack == null) { //如果传入的HttpStack为空,执行下面的操作,生成一个HttpStack对象            if (Build.VERSION.SDK_INT >= 9) { //只分析这种情况,HttpClient在高版本中被废弃了,现在都是用httpurlconnection                stack = new HurlStack();            } else {                // Prior to Gingerbread, HttpUrlConnection was unreliable.                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));            }        }**第一篇准备就到这里打住了,接下来看看HurlStack是何方圣神**        //Network network = new BasicNetwork(stack);       // RequestQueue queue;        //if (maxDiskCacheBytes <= -1)       // {            // No maximum size specified        //  queue = new RequestQueue(new //DiskBasedCache(cacheDir), network); //       }  //      else  //      {            // Disk cache size specified  //        queue = new RequestQueue(new //DiskBasedCache(cacheDir, maxDiskCacheBytes), network);  //      }  //      queue.start();    //    return queue;    }    /**     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.     * You may set a maximum size of the disk cache in bytes.     *     * @param context A {@link Context} to use for creating the cache dir.     * @param maxDiskCacheBytes the maximum size of the disk cache, in bytes. Use -1 for default size.     * @return A started {@link RequestQueue} instance.     */    public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) {        return newRequestQueue(context, null, maxDiskCacheBytes);    }    /**     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.     *     * @param context A {@link Context} to use for creating the cache dir.     * @param stack An {@link HttpStack} to use for the network, or null for default.     * @return A started {@link RequestQueue} instance.     */    public static RequestQueue newRequestQueue(Context context, HttpStack stack)    {        return newRequestQueue(context, stack, -1);    }    /**     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.     *     * @param context A {@link Context} to use for creating the cache dir.     * @return A started {@link RequestQueue} instance.     */    public static RequestQueue newRequestQueue(Context context) {        return newRequestQueue(context, null);    }

2.HurlStack


还是老套路,先上代码

/** * An {@link HttpStack} based on {@link HttpURLConnection}.类的注释写的真好,看到了熟悉的东西了HttpURLConnection,猜测发送网络请求的任务应该都落在了HttpStack的身上,到底猜测的对不对,我们接下来分析得出结论 */public class HurlStack implements HttpStack { *HttpStack  怎么出来就是实现了一个接口啊,有点慌,这个类不会很复杂吧,赶紧cmd进入HttpStack,看看这个接口到底是个什么鬼,进去一看,终于缓了一口气,接口太简单,就不贴出来了*    private static final String HEADER_CONTENT_TYPE = "Content-Type"; //又是网络请求的一个头部参数,我就是不懂,咋的    /**     * An interface for transforming URLs before use.     * 在使用url之前对url做一次转换(什么转换,天知道我不知道,不过看接口我发现我想怎么转换就怎么转换,无非就是string进去string出来。嘎嘎嘎)     */    public interface UrlRewriter {        /**         * Returns a URL to use instead of the provided one, or null to indicate         * this URL should not be used at all.         */        public String rewriteUrl(String originalUrl);    }    private final UrlRewriter mUrlRewriter;    private final SSLSocketFactory mSslSocketFactory; //这个又是什么鬼,我又不知道怎么办,管它呢,暂时就把它当个鬼    public HurlStack() { //管用方式,无参数的方法调用有参数的方法        this(null);    }    /**     * @param urlRewriter Rewriter to use for request URLs     */    public HurlStack(UrlRewriter urlRewriter) {        this(urlRewriter, null);    }    /**     * @param urlRewriter Rewriter to use for request URLs     * @param sslSocketFactory SSL factory to use for HTTPS connections     */    public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {        mUrlRewriter = urlRewriter;        mSslSocketFactory = sslSocketFactory;    }**碰到了一堆鬼,鬼太厉害了,还好不惹他,他也不会影响你,保持好的心情进入真正的主角**    //实现了接口方法,上面实现的那个接口,唯一的作用就是提供了这么一个方法,接收两个参数,1 Request(这不就是Volley二部曲的Request么,恩答案是对的) 2  Map<String, String> additionalHeaders 看参数名称就知道是网络请求的请求头    @Override    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)            throws IOException, AuthFailureError {        //每个request都有一个url吧,毫无疑问        String url = request.getUrl();         //实例化一个map集合,作用是存放请求头信息        HashMap<String, String> map = new HashMap<String, String>();        map.putAll(request.getHeaders());        map.putAll(additionalHeaders);        //一个鬼来了,发现它的作用真的就是对url做一些处理,大部分的时候我们给的url是不需要做什么特殊处理的,想象一个场景,如果一个项目中突然所有请求的url要统一做一种处理,这个时候这个鬼的作用是不是就体现出来了        if (mUrlRewriter != null) {            String rewritten = mUrlRewriter.rewriteUrl(url);            if (rewritten == null) {                throw new IOException("URL blocked by rewriter: " + url);            }            url = rewritten;        }        URL parsedUrl = new URL(url);        //终于看到老朋友了HttpURLConnection,这里通过调用openConnection()方法得到HttpURLConnection对象,迫不及待的想去看看这个方法里到底做了哪些操作,嗖的一下就跳到了该方法        HttpURLConnection connection = openConnection(parsedUrl, request);        //这不又在这里设置请求头了        for (String headerName : map.keySet()) {            connection.addRequestProperty(headerName, map.get(headerName));        }        //上面还在想,请求头设置完成了,是不是该轮到请求正文了,果不其然,来了,手一抖不小心一下子就到方法里了        setConnectionParametersForRequest(connection, request);        //接下来就是对返回报文的处理了,返回HttpResponse对象,到这里这个类基本就算分析完了        // Initialize HttpResponse with data from the HttpURLConnection.        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);        int responseCode = connection.getResponseCode();        if (responseCode == -1) {            // -1 is returned by getResponseCode() if the response code could not be retrieved.            // Signal to the caller that something was wrong with the connection.            throw new IOException("Could not retrieve response code from HttpUrlConnection.");        }        StatusLine responseStatus = new BasicStatusLine(protocolVersion,                connection.getResponseCode(), connection.getResponseMessage());        BasicHttpResponse response = new BasicHttpResponse(responseStatus);        if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {            response.setEntity(entityFromConnection(connection));        }        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;    }    /**     * Checks if a response message contains a body.     * @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3">RFC 7230 section 3.3</a>     * @param requestMethod request method     * @param responseCode response status code     * @return whether the response has a body     */    private static boolean hasResponseBody(int requestMethod, int responseCode) {        return requestMethod != Request.Method.HEAD            && !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK)            && responseCode != HttpStatus.SC_NO_CONTENT            && responseCode != HttpStatus.SC_NOT_MODIFIED;    }    /**     * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.     * @param connection     * @return an HttpEntity populated with data from <code>connection</code>.     */    private static HttpEntity entityFromConnection(HttpURLConnection connection) {        BasicHttpEntity entity = new BasicHttpEntity();        InputStream inputStream;        try {            inputStream = connection.getInputStream();        } catch (IOException ioe) {            inputStream = connection.getErrorStream();        }        entity.setContent(inputStream);        entity.setContentLength(connection.getContentLength());        entity.setContentEncoding(connection.getContentEncoding());        entity.setContentType(connection.getContentType());        return entity;    }    /**     * Create an {@link HttpURLConnection} for the specified {@code url}.     */    protected HttpURLConnection createConnection(URL url) throws IOException {        return (HttpURLConnection) url.openConnection();    }    /**     * Opens an {@link HttpURLConnection} with parameters.     * @param url     * @return an open connection     * @throws IOException     */    private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {    //调用createConnection()方法得到connection对象        HttpURLConnection connection = createConnection(url);         //对connection对象做一一系列的属性设置,还是那句话,不懂我就是不懂这些属性干嘛的,咋的        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);        }//到这里终于看到了我想要返回的对象,真是开心啊,回到前面继续往下走        return connection;    }    @SuppressWarnings("deprecation")    /* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,            Request<?> request) throws IOException, AuthFailureError {        switch (request.getMethod()) { //首先判断请求类型,get 、post、啥的一大堆,不好意思,到目前为止也就只接触了这两个东西,屌丝就屌丝吧,接下来这么多分枝里我要找到一个我熟悉的它            case Method.DEPRECATED_GET_OR_POST:                // This is the deprecated way that needs to be handled for backwards compatibility.                // If the request's post body is null, then the assumption is that the request is                // GET.  Otherwise, it is assumed that the request is a 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;            case Method.HEAD:                connection.setRequestMethod("HEAD");                break;            case Method.OPTIONS:                connection.setRequestMethod("OPTIONS");                break;            case Method.TRACE:                connection.setRequestMethod("TRACE");                break;            case Method.PATCH:                connection.setRequestMethod("PATCH");                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(); //这个在之后才会分析,这里直接给出结论,通过request.getBody()得到a="a"&b="b"这种形式的字符串的字节数组        if (body != null) { //请求正文不为null,将请求正文发送给服务器            connection.setDoOutput(true);            connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());            DataOutputStream out = new DataOutputStream(connection.getOutputStream());            out.write(body);            out.close();        }    }}

总结:到目前为止,看了两个类RequestQueue(分析了部分) ;HurlStack类,知道了这个类的核心作用主要就是实例化HttpURLConnection对象对请求报文和返回报文的处理,其中有许多请求报文头的属性真的也不是很清楚,这也是接下来需要学习的东西之一,但是这并不影响代码的阅读,有些东西不能太较真,只要把重要的整明白就是好的,只有在不断的学习中才能知道自己不懂得东西,才能有方向的学习。

好了,不啰嗦了,洗洗睡觉了。最后声明一句:本人能力有限,写的东西不一定正确,希望如果不小心被你看到了,发现了问题,请赐教,因为本人现在写博客的目的是做笔记,方便自己的学习,能帮助到别人自然更好了。

0 0
原创粉丝点击