美文网首页
Volley学习笔记

Volley学习笔记

作者: 羊角包 | 来源:发表于2017-06-24 19:23 被阅读0次

    Volley简述

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

    Volley基本原理

    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<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry<String, CacheHeader> 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学习笔记

          本文链接:https://www.haomeiwen.com/subject/vabccxtx.html