总结volley源码解析

在阅读之前

本文章默认你已经会使用volley的前提下所总结的, 如果你还未清楚volley的使用, 建议你阅读前面关于volley介绍的文章, 阅读文章之后如果你有任何问题, 欢迎留言交流.

开始吧

  • 首先, 从我们new一个请求队列开始, volley的底层就已经默认开启了5个线程在循环工作等待请求队列的添加, 所以, 我们在使用时必须保证请求队列的全局唯一性, 避免资源的浪费.
  • 从请求队列的构造函数我们可以看出, 构造函数的参数分别有缓存接口Cache; 执行网络请求的接口Network; 网络请求线程池数量threadPoolSize, 默认是4; 还有一个请求结果和错误的分发器ResponseDelivery, 它也是一个接口, 从它接收一个通过Handler绑定主线程的ExecutorDelivery对象可以看出, 它的作用是将网络请求的结果和错误通过Handler交付给主线程处理.
    所有的参数都通过成员变量保存起来.
    从整个构造函数的参数可以看出, 谷歌很充分的利用了面向接口编程, 很大程度上体现了volley具有很强的扩展性.
  • 从缓存接口接收的DiskBasedCache开始, 我们需要创建磁盘缓存, 缓存目录是Cache目录下的volley文件夹, 默认大小是5M.
  • 第二个参数network, 接收一个封装了HttpStack的BasicNetwork对象, 我们来看一下HttpStack接口的实现关系: HttpClientStack和HurlStack实现了这个接口.
  • HttpClientStack内部其实就是通过第三方网络请求工具Apache的HttpClient实现了网络请求, 而HurlStack内部是通过HttpURLConnection发起网络请求的.
  • 在volley类的源码里, 我们可以看到判断如果当前手机系统SDK版本在9之后, 创建HurlStack网络请求, 而在9之前, 则是创建HttpClientStack作为网络请求工具.
  • 谷歌对此也给了解析: 在版本9之前, HttpUrlConnection was unreliable. 也就是说, 使用volley时, 我们不需要关系网络请求是如何实现的, volley已经封装好了最好的选择方案, 保证了volley的稳定性.
  • 所有的参数准备好之后, 才是真正的开始创建了一个RequestQueue, 调用它的start方法.
  • 在start方法里, 我们可以看到new了一个CacheDispatcher缓存分发器和利用for循环默认new了4个NetworkDispatcher网络请求的分发器, CacheDispatcher和NetworkDispatcher继承了Thread类, 说明它们是一个线程.
  • CacheDispatcher创建时传递了4个参数过去, 分别是一个在成员变量声明好的具有优先级的缓存请求队列CacheQueue,一个同样具有优先级的网络请求队列NetworkQueue, 一个之前创建好的磁盘缓存DiskBasedCache和一个ExecutorDelivery.
  • 同样的NetworkDispatcher在创建时传递过去4个参数, 分别是一个网络请求队列, 一个网络请求, 一个磁盘缓存和一个ExecutorDelivery.

现在开始发送网络请求

  • CacheDispatcher的run方法中, 首先就将DiskBasedCache进行初始化, 调用它的initialize方法判断当前缓存目录是否存在, 如果不存在, 则创建缓存目录然后return,如果存在缓存目录, 遍历所有的缓存文件, 读出所有缓存文件的头信息CacheHeader(包含key和过期时间等), 存入头的集合LinkedHashMap.

     private void putEntry(String key, CacheHeader entry) {
        if (!mEntries.containsKey(key)) {
            mTotalSize += entry.size;
        } else {
            CacheHeader oldEntry = mEntries.get(key);
            mTotalSize += (entry.size - oldEntry.size);
        }
        mEntries.put(key, entry);
    }
    
  • 当发生网络请求时, 从集合中获取key, 这个key是请求类型+url组成的字符串, 通过key来获取缓存文件头信息保存到Cache.Entry里.

  • 网络请求首先默认添加到mCacheQueue, CacheDispatcher会一直循环的查询mCacheQueue里有没有请求.
  • 如果拿到了网络请求,便查询有没有缓存

    Cache.Entry entry = mCache.get(request.getCacheKey());
    

    如果没有缓存, 或者缓存已过期, 即”cache-miss”和”cache-hit-expired”, 直接将请求添加到网络请求队列中

    mNetworkQueue.put(request);
    

    如果entry不为null,并且缓存没有过期,即”cache-hit”, 就解析缓存网络请求的响应

     Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
    
        这个parseNetworkResponse方法的实现是交给Request的子类来完成的,因为不同种类的Request解析的方式也肯定不同。
        自定义Request中,parseNetworkResponse()这个方法就是必须要重写的.
    

    分发解析后的结果到主线程

    mDelivery.postResponse(request, response);
    
    因为mDelivery是绑定到主线程的, 所以这个方法是如何在主线程运行的?
    
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {  
        request.markDelivered();  
        request.addMarker("post-response");  
        //传入了一个ResponseDeliveryRunnable对象,就可以保证该对象中的run()方法就是在主线程当中运行的了
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));  
    } 
    
    再看看ResponseDeliveryRunnable的run方法,
    // Deliver a normal response or error, depending.
        if (mResponse.isSuccess()) {
            mRequest.deliverResponse(mResponse.result);
            这个就是我们在自定义Request时需要重写的另外一个方法,每一条网络请求的响应都是回调到这个方法中.
        } else {
            mRequest.deliverError(mResponse.error);
        }
    

    加入到mNetworkQueue后

  • NetworkDispatcher里面也会一直循环的查询mNetworkQueue有没有请求
    如果mNetworkQueue如果有请求,则执行网络请求

    NetworkResponse networkResponse = mNetwork.performRequest(request);
    

    解析网络请求结果

    Response<?> response = request.parseNetworkResponse(networkResponse);
    

    将网络请求的结果存入缓存

    mCache.put(request.getCacheKey(), response.cacheEntry);
    

    将解析后的结果分发给主线程

    mDelivery.postResponse(request, response);
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值