总结volley源码解析

来源:互联网 发布:深圳冰川网络 市场策划 编辑:程序博客网 时间:2024/06/05 05:50

在阅读之前

本文章默认你已经会使用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
原创粉丝点击