universalimageloader-disk cache,缓存网络请求
2015-08-04 15:46
555 查看
看下DiskCache的组成:
转载请标明出处:/article/1908030.html
最顶层的基类是DiskCache,BasicDiskCache对DiskCache做了抽象实现,3个具体的实现类:LimitedAgeDiskCache,UnlimitedDiskCache,LruDiskCache。LruDiskCache比较另类,它内部把请求都代理给了LruDiskCache。看下源码:
重点看下LruDiskCache:
使用一些app的时候会发现,即使是没有网络的时候,页面也是能加载出来的,他们是怎么做到的呢?肯定是对网络请求做了硬盘缓存,这里的DiskCache就非常适合来干这个事情,下面我们就来试一下。
首先,模仿LimitedAgeDiskCache写一个LimitedAgeLruDiskCache,因为缓存的文件我们想让它可以自动过期:
第一次点击按钮,log会输出:from network,接下来继续点击,就会输出:from cache。60秒以后再访问就又是from network了。
转载请标明出处:/article/1908030.html
最顶层的基类是DiskCache,BasicDiskCache对DiskCache做了抽象实现,3个具体的实现类:LimitedAgeDiskCache,UnlimitedDiskCache,LruDiskCache。LruDiskCache比较另类,它内部把请求都代理给了LruDiskCache。看下源码:
public interface DiskCache { File getDirectory(); File get(String imageUri); boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException; boolean save(String imageUri, Bitmap bitmap) throws IOException; boolean remove(String imageUri); void close(); void clear(); }
重点看下LruDiskCache:
public class LruDiskCache implements DiskCache { /** {@value */ public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb /** {@value */ public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG; /** {@value */ public static final int DEFAULT_COMPRESS_QUALITY = 100; private static final String ERROR_ARG_NULL = " argument must be not null"; private static final String ERROR_ARG_NEGATIVE = " argument must be positive number"; protected DiskLruCache cache; private File reserveCacheDir; protected final FileNameGenerator fileNameGenerator; protected int bufferSize = DEFAULT_BUFFER_SIZE; protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT; protected int compressQuality = DEFAULT_COMPRESS_QUALITY; /** * @param cacheDir Directory for file caching * @param fileNameGenerator {@linkplain com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator * Name generator} for cached files. Generated names must match the regex * <strong>[a-z0-9_-]{1,64}</strong> * @param cacheMaxSize Max cache size in bytes. <b>0</b> means cache size is unlimited. * @throws IOException if cache can't be initialized (e.g. "No space left on device") */ public LruDiskCache(File cacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize) throws IOException { this(cacheDir, null, fileNameGenerator, cacheMaxSize, 0); } /** * @param cacheDir Directory for file caching * @param reserveCacheDir null-ok; Reserve directory for file caching. It's used when the primary directory isn't available. * @param fileNameGenerator {@linkplain com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator * Name generator} for cached files. Generated names must match the regex * <strong>[a-z0-9_-]{1,64}</strong> * @param cacheMaxSize Max cache size in bytes. <b>0</b> means cache size is unlimited. * @param cacheMaxFileCount Max file count in cache. <b>0</b> means file count is unlimited. * @throws IOException if cache can't be initialized (e.g. "No space left on device") */ public LruDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long cacheMaxSize, int cacheMaxFileCount) throws IOException { if (cacheDir == null) { throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL); } if (cacheMaxSize < 0) { throw new IllegalArgumentException("cacheMaxSize" + ERROR_ARG_NEGATIVE); } if (cacheMaxFileCount < 0) { throw new IllegalArgumentException("cacheMaxFileCount" + ERROR_ARG_NEGATIVE); } if (fileNameGenerator == null) { throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL); } if (cacheMaxSize == 0) { cacheMaxSize = Long.MAX_VALUE; } if (cacheMaxFileCount == 0) { cacheMaxFileCount = Integer.MAX_VALUE; } this.reserveCacheDir = reserveCacheDir; this.fileNameGenerator = fileNameGenerator; initCache(cacheDir, reserveCacheDir, cacheMaxSize, cacheMaxFileCount); } private void initCache(File cacheDir, File reserveCacheDir, long cacheMaxSize, int cacheMaxFileCount) throws IOException { try { cache = DiskLruCache.open(cacheDir, 1, 1, cacheMaxSize, cacheMaxFileCount);//这里的版本号给固定成了1,据说因此还导致会踩坑:http://blog.csdn.net/shaw1994/article/details/47223133 } catch (IOException e) { L.e(e); if (reserveCacheDir != null) { initCache(reserveCacheDir, null, cacheMaxSize, cacheMaxFileCount); } if (cache == null) { throw e; //new RuntimeException("Can't initialize disk cache", e); } } } @Override public File getDirectory() { return cache.getDirectory(); } @Override public File get(String imageUri) { DiskLruCache.Snapshot snapshot = null; try { snapshot = cache.get(getKey(imageUri)); return snapshot == null ? null : snapshot.getFile(0); } catch (IOException e) { L.e(e); return null; } finally { if (snapshot != null) { snapshot.close(); } } } @Override public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException { DiskLruCache.Editor editor = cache.edit(getKey(imageUri)); if (editor == null) { return false; } OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize); boolean copied = false; try { copied = IoUtils.copyStream(imageStream, os, listener, bufferSize); } finally { IoUtils.closeSilently(os); if (copied) { editor.commit(); } else { editor.abort(); } } return copied; } @Override public boolean save(String imageUri, Bitmap bitmap) throws IOException { DiskLruCache.Editor editor = cache.edit(getKey(imageUri)); if (editor == null) { return false; } OutputStream os = new BufferedOutputStream(editor.newOutputStream(0), bufferSize); boolean savedSuccessfully = false; try { savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os); } finally { IoUtils.closeSilently(os); } if (savedSuccessfully) { editor.commit(); } else { editor.abort(); } return savedSuccessfully; } @Override public boolean remove(String imageUri) { try { return cache.remove(getKey(imageUri)); } catch (IOException e) { L.e(e); return false; } } @Override public void close() { try { cache.close(); } catch (IOException e) { L.e(e); } cache = null; } @Override public void clear() { try { cache.delete(); } catch (IOException e) { L.e(e); } try { initCache(cache.getDirectory(), reserveCacheDir, cache.getMaxSize(), cache.getMaxFileCount()); } catch (IOException e) { L.e(e); } } private String getKey(String imageUri) { return fileNameGenerator.generate(imageUri); } public void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; } public void setCompressFormat(Bitmap.CompressFormat compressFormat) { this.compressFormat = compressFormat; } public void setCompressQuality(int compressQuality) { this.compressQuality = compressQuality; } }对DiskLruCache做了简单的封装,使用更加方便了。
使用一些app的时候会发现,即使是没有网络的时候,页面也是能加载出来的,他们是怎么做到的呢?肯定是对网络请求做了硬盘缓存,这里的DiskCache就非常适合来干这个事情,下面我们就来试一下。
首先,模仿LimitedAgeDiskCache写一个LimitedAgeLruDiskCache,因为缓存的文件我们想让它可以自动过期:
public class LimitedAgeLruDiskCache extends LruDiskCache { /**过期时间,秒*/ private final long maxFileAge; private final Map<File, Long> loadingDates = Collections.synchronizedMap(new HashMap<File, Long>()); public LimitedAgeLruDiskCache(File cacheDir, long cacheMaxSize,int cacheMaxFileCount, long maxAge) throws IOException { super(cacheDir, null, new Md5FileNameGenerator(), cacheMaxSize, cacheMaxFileCount); this.maxFileAge = maxAge * 1000; } @Override public File get(String imageUri) { File file = super.get(imageUri); if (file != null && file.exists()) { boolean cached; Long loadingDate = loadingDates.get(file); if (loadingDate == null) { cached = false; loadingDate = file.lastModified(); } else { cached = true; } if (System.currentTimeMillis() - loadingDate > maxFileAge) { super.remove(imageUri); loadingDates.remove(file); } else if (!cached) { loadingDates.put(file, loadingDate); } } return file; } @Override public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException { boolean saved = super.save(imageUri, imageStream, listener); rememberUsage(imageUri); return saved; } @Override public boolean remove(String imageUri) { loadingDates.remove(getFile(imageUri)); return super.remove(imageUri); } @Override public void clear() { super.clear(); loadingDates.clear(); } private void rememberUsage(String imageUri) { File file = getFile(imageUri); long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime); loadingDates.put(file, currentTime); } }接下来我们做更进一步的封装,从缓存get()出来的是一个对象:
public class RequestCache { private static volatile RequestCache instance; private LimitedAgeLruDiskCache mLruCache; private RequestCache(){ } /** * 应用启动的时候调用 * */ public static void init(Application context) { File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context, "request-cache"); try { getInstance().mLruCache = new LimitedAgeLruDiskCache(individualCacheDir, 10 * 1024 * 1024, 1024, 60);//缓存有效期默认60秒 } catch (IOException e) { L.e(e); throw new RuntimeException("初始化DiskLruCache失败", e); } } public static RequestCache getInstance(){ if(instance == null){ synchronized (RequestCache.class) { if(instance == null){ instance = new RequestCache(); } } } return instance; } public static <T> T get(String url, Class<T> clazz){ File file = getInstance().mLruCache.get(url); if(file != null && file.length() > 0){ String jsondata = FileUtil.getContent(file, "UTF-8"); JSONObject json = JSON.parseObject(jsondata); return JSON.toJavaObject(json, clazz); }else{ return null; } } public static boolean put(String url, String string){ try{ ByteArrayInputStream in = new ByteArrayInputStream(string.getBytes("UTF-8")); return getInstance().mLruCache.save(url, in, null); }catch(Exception e){ e.printStackTrace(); return false; } } public static boolean put(String url, Object obj){ String str = JSON.toJSONString(obj); return put(url, str); } public static boolean remove(String url){ return getInstance().mLruCache.remove(url); } public static void clear(){ getInstance().mLruCache.clear(); } }假如响应数据是json格式,我们用fastjson做json序列化。客户端在使用的时候:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); RequestCache.init(this.getApplication()); Button upload = (Button) this.findViewById(R.id.upload); upload.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String url = "http://172.16.28.250:8080/web/user.jsp?username=xjs&password=123456"; doRequest(url); } }); } public void doRequest(final String url){ new AsyncTask<String,Integer,Person>(){ @Override protected Person doInBackground(String... params) { Person p = RequestCache.get(url, Person.class);//先从cache中去获取 if(p == null){ String s = HttpUtil.get(params[0]);//获取不到去网络获取 JSONObject json = JSON.parseObject(s); p = JSON.toJavaObject(json, Person.class); Log.e("test", "from network"); if(p != null){ RequestCache.put(url, p); //存到cache中 } }else{ Log.e("test", "from cache"); } return p; } @Override protected void onPostExecute(Person result) { super.onPostExecute(result); Toast.makeText(MainActivity.this, result.toString(), Toast.LENGTH_SHORT).show(); } }.execute(url); }
第一次点击按钮,log会输出:from network,接下来继续点击,就会输出:from cache。60秒以后再访问就又是from network了。
相关文章推荐
- tcp retransmission问题
- HttpServletResponse对象
- TCP协议详解(四)
- [网络流24题] 05 圆桌聚餐(最大流判满流)
- unp.h及网络编程UNP所有代码的下载
- HTTP协议小结
- HTTP协议小结
- 在Visual Studio 2010里面使用.NET 4.5里面新增加的HttpClient
- HTTP 304 返回状态码
- centos 配置svn http serve
- 自己动手写路由器之ioctl获取网络接口信息
- cocos HttpClient enableCookies 大坑
- tcpDump 抓包保存
- android网络通信之WIFI教程实例汇总
- Netty Http server & Client
- socket通信之socket的通信过程
- linux c 笔记 网络编程(一)
- HTTP协议之http状态码详解 300-305 重定向
- HTTPURLCONNECTION使用
- VC++获取某个http网页内容