开源项目学习之Volley(一)

一. 重要的类

1.1 RequestQueue

重要变量

    
    //请求等待集合,队列中的请求是重复的,之前已经有一个相同的请求正在执行
    private final Map>> mWaitingRequests =
            new HashMap<>();

    private final Set> mCurrentRequests = new HashSet>();

    //缓存队列
    private final PriorityBlockingQueue> mCacheQueue =
            new PriorityBlockingQueue<>();

    //网络请求队列
    private final PriorityBlockingQueue> mNetworkQueue =
            new PriorityBlockingQueue<>();
            
    //默认使用的是DiskBasedCache
    private final Cache mCache;

    /** 默认使用BasicNetwork */
    private final Network mNetwork;

    /** 默认使用ExecutorDelivery */
    private final ResponseDelivery mDelivery;

    //网络请求线程数组
    private final NetworkDispatcher[] mDispatchers;

    //缓存线程
    private CacheDispatcher mCacheDispatcher;

重要方法

  • add(), 可以将请求放入队列中
  • start(), RequestQueue还负责启动cache线程和network线程
  • stop(), 停止缓存线程和网络线程
  • finish(), 请求结束后的回调

1.2 Request

抽象了Http请求, Request本身是一个抽象类,继承它的子类必须要实现两个抽象方法:

  • abstract protected Response parseNetworkResponse(NetworkResponse response) - 将http请求返回的响应解析成合适的类型
  • abstract protected void deliverResponse(T response) - 将解析好的响应传递给监听器

每一个Request的缓存key就是它的Url, 如果要创建POST或者PUT请求, 也可以重写以下两个方法:

  • public byte[] getBody() throws AuthFailureError {}
  • public Map getParams() throws AuthFailureError

getBody()方法默认也是通过getParams方法来创建Body内容,而getParams方法默认实现是直接返回null,所以一般构建POSTPUT请求应当重写这两个方法中的一个, 一般直接重写getParams即可

子类

  • StringRequest, 返回值为String类型
  • JsonRequest, 代表了Body为json的请求, 本身也是一个抽象类
  • JsonObjectRequest, 继承了JsonRequest, 将返回值解析为JSONObject
  • JsonArrayRequest, 继承了JsonRequest, 将返回值解析为JsonArrayRequest
  • ImageRequest, 图片请求,将返回值解析成为Bitmap

1.3 HttpStack

HttpStack是一个接口,就定义了一个执行方网络请求的方法performRequest, 它有两个实现类HurlStackHttpClientStack

HurlStack

HurlStackperformRequest主要是用HttpUrlConnection来实现网络请求,这也是Android官方目前推荐使用的方式

HttpClientStack

HttpClientStack是使用Apache的HttpClient来进行网络请求,这一方式目前目前不被Android官方推荐,HttpClient也已从Android源码中移除

1.4 BasicNetwork

BasicNetwork实现了Network接口,其内部含有HttpStack
引用,其performRequest主要工作是解析请求的Header,调用HttpStack.performRequest来真正执行网络请求, 之后判断http响应码(主要是判断304,跟缓存相关),生成NetworkResponse

1.5 DiskBasedCache

磁盘缓存类,实现了Cache接口

重要方法

  • public synchronized void initialize(),初始化缓存,扫面缓存目录(默认为/data/data/pkg_name/files/cache/volley)得到缓存数据生成缓存header并放入内存
  • public synchronized Entry get(String key), 从缓存中获取缓存header,然后读取缓存文件
  • public synchronized void put(String key, Entry entry), 先检查如果给当前Entry分配空间以后, 缓存是否会满,如果会满则遍历并逐个删除缓存,直到如果为当前Entry分配空间以后,缓存量小于最大缓存量的0.9, 然后再新建缓存文件

二. 基本流程

2.1 Volley.newRequestQueue

    
    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        //创建缓存目录,默认在/data/data/pkg_name/files/cache/volley
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                //使用HttpUrlConnection的方式进行网络请求
                stack = new HurlStack();
            } else {
                //使用HttpClient进行http请求
                ...
            }
        }
        //使用默认的BasicNetwork
        Network network = new BasicNetwork(stack);
        //创建请求队列, 默认使用ExecutorDelivery
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        //启动CacheDispatcher和NetworkDispatcher线程,开始接收请求,[2]
        queue.start();

        return queue;
    }

  1. 创建缓存目录, 默认为/data/data/pkg_name/files/cache/volley
  2. 创建对应的HttpStack
  3. 创建BasciNetwork
  4. 创建DiskBasedCache
  5. 创建RequestQueue, 并启动

2.2 RequestQueue.start


    public void start() {
        //停止所有缓存线程和网络请求线程, 
        stop();
        //创建缓存线程,并启动线程. [3]
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        //创建网络请求线程数组中所有的网络请求线程,并启动, [4]
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

  1. mCacheQueuePriorityBlockingQueue>类型
  2. mNetworkQueue也是PriorityBlockingQueue>类型
  3. CacheDispatcherNetworkDispatcher都是继承自Thread即一个线程类

2.3 CacheDispatcher.run

    
    @Override
    public void run() {
        //设置缓存线程的优先级
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // 初始化DiskBasedCache, [2.3.1]
        mCache.initialize();

        while (true) {
            try {
                /* 从缓存队列中读取Request, 如果队列中没有请求,则线程阻塞*/
                final Request request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                /* 如果请求已经被取消,则直接结束当前请求, 从当前请求集合以及等待请求列表中删除*/
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                /* 判断当前请求有没有缓存, request的cacheKey就是url*/
                Cache.Entry entry = mCache.get(request.getCacheKey());
                /* 没有缓存结果,则直接交友网络请求线程*/
                if (entry == null) {
                    request.addMarker("cache-miss");
                    mNetworkQueue.put(request);
                    continue;
                }

                /* 如果有缓存结果,但缓存结果已经失效,同样交由网络线程*/
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                request.addMarker("cache-hit");
                /* 缓存命中,则调用Request的parseNetworkResponse获取相应的Response*/
                Response response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                if (!entry.refreshNeeded()) {
                    /* 如果缓存不需要刷新,则直接传递结果*/
                    mDelivery.postResponse(request, response);
                } else {
                    /* 如果缓存还需要刷新,传递响应结果,将将请求交由网络线程进行新鲜度验证*/
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    response.intermediate = true;
                    
                     /* 向网络请求队列中添加请求*/
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
            }
        }
    }

  1. 初始化缓存,获取缓存内容
  2. 开启无限循环,从PriorityBlockingQueue中取出请求,查看请求是否有缓存(请求的key就是url),如果队列中没有请求,则线程阻塞
  3. 如果没有缓存命中,则将请求放入网络队列,去执行网络请求
  4. 如果有缓存命中,检查缓存是否失效,如果失效也放入网络队列,去执行网络请求
  5. 如果缓存命中且有效,调用Request.parseNetworkResponse获取相应的Response
  6. 判断缓存需不要刷新,如果不需要刷新,则直接调用ExceutorDelivery.postResponse传递Response;如果缓存还需要刷新,则还是将请求放入网络请求队列,去执行网络请求获取最新结果

2.3.1 DiskBasedCache.initialize

    @Override
    public synchronized void initialize() {
        /* 如果缓存目录不存在,则创建缓存目录 */
        if (!mRootDirectory.exists()) {
            if (!mRootDirectory.mkdirs()) {
                VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
            }
            return;
        }
        /* 获取缓存目录下所有文件*/
        File[] files = mRootDirectory.listFiles();
        if (files == null) {
            return;
        }
        /* 遍历多有缓存文件,如果能够正常读取,则为每个缓存文件生成一个CacheHeader
        * 并放入集合中*/
        for (File file : files) {
            BufferedInputStream fis = null;
            try {
                fis = new BufferedInputStream(new FileInputStream(file));
                CacheHeader entry = CacheHeader.readHeader(fis);
                entry.size = file.length();
                putEntry(entry.key, entry);
            } catch (IOException e) {
                /* 如果发生异常,证明该文件不符合Volley的缓存格式
                * 删除该文件*/
                if (file != null) {
                   file.delete();
                }
            } finally {
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException ignored) { }
            }
        }
    }

2.4 NetworkDispatcher.run


    @Override
    public void run() {
        //设置该网络线程的优先级
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request request;
            try {
                // 从网络请求队列中获取请求
                request = mQueue.take();
            } catch (InterruptedException e) {
                ...
                continue;
            }

            try {
                ...
                /* 如果请求已经被取消,则直接结束当前请求, 从当前请求集合以及等待请求列表中删除*/
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }
                /* 设置网络流量标识*/
                addTrafficStatsTag(request);

                /* 执行网络请求, 并获取对应的Response [2.4.1]*/ 
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                /* 如果服务端返回304同时我们已经传输了Response, 则结束该Request避免重复传输Response*/
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                /* 调用Request的parseNetworkResponse解析结果*/
                Response response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                /* 如果Request允许缓存且缓存实体不为空,则调用DiskBasedCache进行缓存 */
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                /* 标记Request已传输结果 */
                request.markDelivered();
                /* ExecutorDelivery 传输结果 */
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                ... //分发VolleyError
            } catch (Exception e) {
                ... //分发VolleyError
            }
        }
    }


  1. 从网络请求队列中取出请求,如果没有请求则线程会一直阻塞
  2. 调用BasicNetwork.performRequest执行网络请求,并获取相应的Response
  3. 如果Request允许缓存且缓存实体不为空,则将Request放入缓存
  4. 调用ExecutorDelivery.postResponse传输结果

2.4.1 BasicNetwork.performRequest


    @Override
    public NetworkResponse performRequest(Request request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map responseHeaders = Collections.emptyMap();
            try {
                // Gather headers.
                Map headers = new HashMap<>();
                //如果请求中包含缓存字段,存储进headers
                addCacheHeaders(headers, request.getCacheEntry());
                //调用HttpStack.performRequest, 一般情况下,HttpStack都会是HurlStack
                //[2.4.2]
                httpResponse = mHttpStack.performRequest(request, headers);

                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();
                //解析Header
                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // 返回码是304
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);
                    }

                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // Some responses such as 204s do not have content.  We must check.
                if (httpResponse.getEntity() != null) {
                  responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                  // Add 0 byte response as a way of honestly representing a
                  // no-content request.
                  responseContents = new byte[0];
                }

                ...

                if (statusCode < 200 || statusCode > 299) {
                    throw new IOException();
                }
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false,SystemClock.elapsedRealtime() - requestStart);
            } catch (SocketTimeoutException e) {
                //[2.4.3]
                attempRetryonExcption("socket", request, new TimeoutError());
            } catch .... {//其他excption
                .... //基本都是attempRetryonExcption
            }
        }
    }

2.4.2 HurlStack.performRequest

由于目前Android平台基本都是使用HttpUrlConnection, 所以这里就默认使用HurlStack, 不考虑HttpClientStack

    @Override
    public HttpResponse performRequest(Request request, Map additionalHeaders) throws IOException, AuthFailureError {
        //设置Header
        String url = request.getUrl();
        HashMap map = new HashMap<>();
        map.putAll(request.getHeaders());
        //additionalHeader一般都是用来缓存的Header
        map.putAll(additionalHeaders);
        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);
        //建立Http连接[2.4.2.1]
        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) {
           ...
        }
        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;
    }
2.4.2.1 HurlStack.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
        // 如果scheme是https,设置SSLSocketFactory
        if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
            ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
        }

        return connection;
    }

2.4.3 BasicNetwork.attemptRetryOnException


    private static void attemptRetryOnException(String logPrefix, Request request,
            VolleyError exception) throws VolleyError {
        //默认是DefaultRetryPolicy
        RetryPolicy retryPolicy = request.getRetryPolicy();
        int oldTimeout = request.getTimeoutMs();

        try {
            //DefaultRetryPolicy中的retry主要就是判断重试次数是否已经到达上限
            retryPolicy.retry(exception);
        } catch (VolleyError e) {
            request.addMarker(
                    String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
            throw e;
        }
        request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
    }


2.5. ExecutorDelivery.postResponse

    @Override
    public void postResponse(Request request, Response response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        //向线程池中提交任务
        //[2.5.1]
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

2.5.1 ResponseDeliveryRunnable.run()


        @Override
        public void run() {
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // Deliver a normal response or error, depending.
            if (mResponse.isSuccess()) {
                //如果成功会回调Response.Listener的onResponse
                mRequest.deliverResponse(mResponse.result);
            } else {
                //如果失败会回调Response.ErrorListener的onErrorResponse
                mRequest.deliverError(mResponse.error);
            }

            if (mResponse.intermediate) {
                //如果这是一个intermediate Response,添加Marker
                mRequest.addMarker("intermediate-response");
            } else {
                //结束当前请求
                mRequest.finish("done");
            }

            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }

你可能感兴趣的:(开源项目学习之Volley(一))