Volley学习笔记

Volley简述

Volley是Google推出的用于Android下发起Http请求的网络库。它适合于频繁的,数据量较小的通信,而在数据量大的场景下表现不佳,原因在于,Volley对于所有的Response的解析都是在内存当中做的。官方推荐在对于大量数据通信的场景下选择其他的库,例如DownloadManager.

Volley基本原理

Volley学习笔记_第1张图片
Android+Volley+主要对象关系图.png

看图说话:
①: Volley的主要使用方法就是创建Request实例,并且将其加入到RequestQueue当中。在RequestQueue中维护了3个队列和一个Map,图中只标识最重要的两个队列NetWorkQueue和CacheQueue。如果Request不允许使用缓存,则会被直接加入到NetWorkQueue,否则被加入CacheQueue。
②:CacheDispatcher运行在单独的线程当中,不停的尝试获取CacheQueue中的Request对象。
③:CacheDispatcher一旦获取Request对象成功,就派发到Cache对象尝试获取缓存。如果找到,则直接返回Response,否则CacheDispatcher还是会将此Request对象放置于NetWorkQueue。
④: RequestQueue默认开启4个NetWorkDispatcher线程,同样不停的尝试获取NetWorkQueue当中的Request对象。
⑤: 其中某个NetWorkDispatcher获取Request对象成功,将其派发给HttpStack对象处理。HttpStack对象是真正发送接收Http报文的对象,用户可以实现它的接口,自定义发送与接收的过程。Volley默认使用HttpClient或者HttpURLConnection(根据不同SDK版本)。
⑥: HttpStack处理发送与接收。即传入Request对象,返回Response对象。

最后,无论是NetWorkDispatcher还是CacheDispatcher最后都回调Request的deliverResponse()。这也是官方文档所说的,为什么自定义的Request必须实现此方法的原因。需要注意的是,Request中必须实现的两个方法是在子线程中执行的。

Volley目录结构

volley
    │  AuthFailureError.java
    │  TimeoutError.java
    │  VolleyError.java
    │  ServerError.java
    │  ClientError.java
    │  ParseError.java
    │  NetworkError.java
    │  NoConnectionError.java
------------------------------------------------------------------------------------------
    │  Cache.java //Cache接口
    │  CacheDispatcher.java     
    │  DefaultRetryPolicy.java //重试策略,当请求发送遭遇异常,回调Request中定义的重试方法
    │  ExecutorDelivery.java //ResponseDelivery实现类
    │  Network.java
    │  NetworkDispatcher.java
------------------------------------------------------------------------------------------
    │  NetworkResponse.java //解析HTTP报文的Response数据结构
    │  Request.java//定义了Resquest数据结构
    │  RequestQueue.java//定义了RequestQueue数据结构
    │  Response.java //在本地表示的Response,由NetworkResponse解析产生,包括数据和缓存信息
------------------------------------------------------------------------------------------
    │  ResponseDelivery.java //接口
    │  RetryPolicy.java  //接口
    │  VolleyLog.java
    └─toolbox
            AndroidAuthenticator.java
            Authenticator.java
------------------------------------------------------------------------------------------
            BasicNetwork.java // Network实现类
            RequestFuture.java
------------------------------------------------------------------------------------------
            DiskBasedCache.java //Cache实现类
            NoCache.java//Cache实现类,即不使用缓存
------------------------------------------------------------------------------------------
            HttpStack.java//接口
            HttpClientStack.java //HttpStack实现类,使用HttpClient
            HurlStack.java//HttpStack实现类,使用HttpURLConnection
------------------------------------------------------------------------------------------
            ClearCacheRequest.java//Request的一种,用于清除所有缓存数据
            StringRequest.java//Request的一种,用于请求String数据
            JsonRequest.java //Request的一种,用于请求JSON数据
            JsonArrayRequest.java
            JsonObjectRequest.java
------------------------------------------------------------------------------------------
            ImageRequest.java//Request的一种,用于加载图片
            ImageLoader.java
            NetworkImageView.java
------------------------------------------------------------------------------------------
            HttpHeaderParser.java//工具类,可用于解析报文头部缓存信息
            Volley.java//工具类,方便创建RequestQueue
            PoolingByteArrayOutputStream.java
            ByteArrayPool.java//工具类,Byte数组缓存池

细节剖析 -> 缓存存取的具体实现

一. 缓存的组织形式:
DiskBasedCache是将缓存按一定格式写入文件,所以在创建DiskBasedCache需要传入指定文件路径。按照代码逻辑,每一个Cache项都单独存放在一个文件当中。初始化DiskBasedCache时,读取每一个文件中的缓存头部内容,然后保存在内存Map中。

//摘取代码关键部分,与实际代码不同
@Override
public synchronized void initialize() {
   File[] files = mRootDirectory.listFiles();
   for (File file : files) {
      fis = new BufferedInputStream(new FileInputStream(file));
      CacheHeader entry = CacheHeader.readHeader(fis);
      entry.size = file.length();
      mEntries.put(key, entry);
    }
}  

二. 获取缓存
CacheDispatcher获得Request对象,就尽力获取缓存内容。

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

DiskBasedCache中,首先从内存中查找缓存头部是否存在,如果存在,再去文件中读取缓存的具体内容。

@Override
public synchronized Entry get(String key) {
    CacheHeader entry = mEntries.get(key);
    // if the entry does not exist, return.
    if (entry == null) {
        return null;
    }
    try {
        cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
        CacheHeader.readHeader(cis); // eat header
        byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
        return entry.toCacheEntry(data);
    }
}

三. Cache Prune
Volley会记录缓存使用空间大小,当缓存空间不足时,触发Cache Prune。遍历Map,按照插入顺序进行删除,直到空间充足。

Iterator> iterator = mEntries.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry entry = iterator.next();
    CacheHeader e = entry.getValue();
    boolean deleted = getFileForKey(e.key).delete();
    if (deleted) {
        mTotalSize -= e.size;
    } else {
       VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
               e.key, getFilenameForKey(e.key));
    }
    iterator.remove();
    prunedFiles++;

    if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
        break;
    }
}

四. 缓存过期
判断缓存过期的两条规则:

  1. 当缓存信息的ttl小于当前系统时间,则认为过期。
  2. 缓存信息的soft_ttl小于当前系统时间,则认为过期

第二条规则有助于认为更改过期信息,强制系统进行缓存更新。
当Volley未找到缓存项或者缓存过期,则将Request加入NetworkQueue。但是很多网站采用ETag标签标识实体有无改变,在这种情况下,Volley仍然会发送网络请求,并根据返回304进行处理。下文将分析这种情况。

细节剖析 -> NetworkDispatcher或者CacheDispatcher完成后如何切换线程

ResponseDelivery持有MainThread的Handler引用,当一切都处理完毕,ResponseDelivery将Runnable对象发送给主线程的Handler。而此Runnable对象的任务就是回调Request的监听器。这里有两个非常重要的方法会被回调,一个是Request子类必须实现的deliverResponse,另外一个是可选的实现RequestFinishedListener接口的onRequestFinished,此方法的调用时机就是当Request被处理完毕。正常情况下,deliverResponse的调用时机在onRequestFinished之前。另外,当Request被中止时,onRequestFinished也会被触发。

细节剖析-> Volley处理缓存相关网络请求

如上节所述,当采用ETag标签时,即便Cache没有过期,Volley还是会发送网络请求的。
NetworkDispacher从队列中取得Request,然后交给Network发送。Network首先对Request添加Cache头,If-None-Match: Etag,并发送。NetworkDispatcher对接收到的回复报文进行处理,刷新缓存。
对于每一次的请求,Request都会回调deliverResponse,但是对于304的回复报文,什么内容都没有,所以,对于304回复正常报文,Volley会从cache中构造Response。

总结

Volley代码非常简洁易懂,模块之间划分清晰,适合学习~

你可能感兴趣的:(Volley学习笔记)