android volley缓存设计分析

来源:互联网 发布:mac版flashplayer 编辑:程序博客网 时间:2024/04/30 04:32
本文学习一下volley的缓存设计写法。
首先看下哪些类使用了cache。
Cache.java
public interface Cache {
public Entry get(String key);
public void put(String key, Entry entry);
public void initialize();
public void invalidate(String key, boolean fullExpire);
public void remove(String key);
public void clear();
public static class Entry {
      /** The data returned from cache. */
       public byte[] data;
       /** ETag for cache coherency. */
       public String etag;
       /** Date of this response as reported by the server. */
       public long serverDate;
       /** The last modified date for the requested object. */
       public long lastModified;
       /** TTL for this record. */
       public long ttl;
       /** Soft TTL for this record. */
       public long softTtl;
       /** Immutable response headers as received from server; must be non-null. */
       public Map<String, String> responseHeaders = Collections.emptyMap();
       /** True if the entry is expired. */
       public boolean isExpired() {
           return this.ttl < System.currentTimeMillis();
       }
       /** True if a refresh is needed from the original data source. */
       public boolean refreshNeeded() {
           return this.softTtl < System.currentTimeMillis();
       }
   }
}

DiskBasedCache是唯一的实现类。
DiskBasedCache.java
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;            //路径存在 但不存在子目录即可返回
   }
   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);    //若路径下的子路径不为空 则将子路径处理成cache
       } catch (IOException e) {
           if (file != null) {
              file.delete();
           }
       } finally {
           try {
               if (fis != null) {
                   fis.close();
               }
          } catch (IOException ignored) { }
       }
   }
}

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);    //mEntries—>new LinkedHashMap<String,CacheHeader>(16,.75f,true);
                        //volley是这样做的 在内存中保留一个map 在硬盘中存储实际的内容
}

@Override
public synchronized void put(String key, Entry entry) {
   pruneIfNeeded(entry.data.length);
   File file = getFileForKey(key);
   try {
       BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
       CacheHeader e = new CacheHeader(key, entry);//将除data外的信息保存到cacheHeader进而保存到map 内存
       boolean success = e.writeHeader(fos);               //将header信息写入硬盘
       if (!success) {
           fos.close();
           VolleyLog.d("Failed to write header for %s", file.getAbsolutePath());
           throw new IOException();
       }
       fos.write(entry.data);                                        //将data写入硬盘
       fos.close();
       putEntry(key, e);
       return;
   } catch (IOException e) {
   }
   boolean deleted = file.delete();
   if (!deleted) {
       VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
   }
}

看一个cache put行为的实例。
NetworkDispatcher.java
public void run()
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
if (request.shouldCache() && response.cacheEntry != null) {
   mCache.put(request.getCacheKey(), response.cacheEntry);
   request.addMarker("network-cache-written");
}

看一个Response实现parseNetworkResponse的例子。
ImageRequest.java
protected Response<Bitmap> parseNetworkResponse(NetworkResponse response)
return doParse(response);

private Response<Bitmap> doParse(NetworkResponse response)
return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));  
                   //由下面的接口知 第二个参数就是cache.Entry—>回忆一下 自定义GsonRequest最后也用到了这个
      //具体实现类HttpHeaderParser 在com.android.volley.toolbox包下 此处不做分析 就是构造出一个entry而已
     /////////////******
Response.java
public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
   return new Response<T>(result, cacheEntry);
}
   *****///////////

回到DiskBasedCache.java
static class CacheHeader {
   public long size;                                              //观察到这些字段和Cache Entry是一样的 唯独少了data字段
   /** The key that identifies the cache entry. */
   public String key;
   /** ETag for cache coherence. */
   public String etag;
   /** Date of this response as reported by the server. */
   public long serverDate;
   /** The last modified date for the requested object. */
   public long lastModified;
   /** TTL for this record. */
   public long ttl;
   /** Soft TTL for this record. */
   public long softTtl;
   /** Headers from the response resulting in this cache entry. */
public Map<String, String> responseHeaders;  //观察到这些字段和Cache Entry是一样的 唯独少了data字段
public CacheHeader(String key, Entry entry);                    //构造函数
public static CacheHeader readHeader(InputStream is);  //读取header 其实是剔除文件中header部分的内容
public Entry toCacheEntry(byte[] data);                            //根据data还原出一个Entry对象
public boolean writeHeader(OutputStream os);                //写header
}

最后看一下get的实现
@Override
public synchronized Entry get(String key) {
   CacheHeader entry = mEntries.get(key);
   // if the entry does not exist, return.
   if (entry == null) {
       return null;
   }

    File file = getFileForKey(key);
   CountingInputStream cis = 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);
   } catch (IOException e) {
       VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
       remove(key);
       return null;
   }  catch (NegativeArraySizeException e) {
       VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
       remove(key);
       return null;
   } finally {
       if (cis != null) {
           try {
               cis.close();
           } catch (IOException ioe) {
               return null;
           }
       }
   }
}            //没啥特别

ok~这篇文章就先到这里啦~
这篇文章分析了volley关于cache的实现。
1 volley封装了一个Cache接口 用于操作cache比如插入cache 清除cache等
2 volley封装了的Cache.Entry实体用于保存cache实际的内容 比如cache中内容从服务器获取的时间 cache
  的内容等
3 为了构建cache.Entry实体 volley封装了一个HttpHeaderParser类 在http请求成功时将http请求 比如请求头 data封装成一个Cache.Entry。
4 volley为了加快cache的查找速度保存了一个类型为LinkedHashMap<String,CacheHeader>的变量保存在内容中。
5 volley再次封装Cache.Entry 封装出一个CacheHeader保存cache.Entry除data外的内容(这样可以作为map的value) 而将实际data存储在硬盘。
0 0
原创粉丝点击