Volley尝试源码解析1

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 additionalHeaders 看参数名称就知道是网络请求的请求头
    @Override
    public HttpResponse performRequest(Request request, Map additionalHeaders)
            throws IOException, AuthFailureError {
        //每个request都有一个url吧,毫无疑问
        String url = request.getUrl(); 
        //实例化一个map集合,作用是存放请求头信息
        HashMap map = new HashMap();
        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> 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 RFC 7230 section 3.3
     * @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 connection.
     */
    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对象对请求报文和返回报文的处理,其中有许多请求报文头的属性真的也不是很清楚,这也是接下来需要学习的东西之一,但是这并不影响代码的阅读,有些东西不能太较真,只要把重要的整明白就是好的,只有在不断的学习中才能知道自己不懂得东西,才能有方向的学习。

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

你可能感兴趣的:(volley源码解析,volley)