在使用ListView,GridView控件时,由于其内部的重用机制,导致item中的内容会被清空,但是如果是网络中下载的内容特别是图片则会比较麻烦,因为经常需要从后台重新加载。为了提高用户体验,需要对图片等数据进行缓存,避免耗时的网络操作。
一、LruCache:android官方提供的用于在内存中进行缓存的一个类。
1 public LruCache(int maxSize) { 2 if (maxSize <= 0) { 3 throw new IllegalArgumentException("maxSize <= 0"); 4 } 5 this.maxSize = maxSize; 6 this.map = new LinkedHashMap<K, V>(0, 0.75f, true); 7 }
由构造函数可知,
1、该缓存系统是使用LinkedHashMap来保存数据的。
2、需要传入一个int型的数值表示可以存储的最大容量,但是由于存入的数据的类型没有限制,所以添加和删除数据时的size变化就不确定。例如缓存String就可以以个数计算;但是如果存入的是Bitmap,则以其的内存大小计算更合适。!!!所以,在使用该类时通常需要重写其sizeOf(),指定其size变化的规则。
该缓存类实现的效果是:先保存然后进行调整,如果超过容量则将最近最早未被使用的数据删除。
1 public final V put(K key, V value) { 2 if (key == null || value == null) { 3 throw new NullPointerException("key == null || value == null"); 4 } 5 6 V previous; 7 //该同步块中执行put操作,并对size进行调整,一种是覆盖原有key,一种是新建 8 synchronized (this) { 9 putCount++; 10 size += safeSizeOf(key, value); 11 previous = map.put(key, value); 12 if (previous != null) { 13 size -= safeSizeOf(key, previous); 14 } 15 } 16 17 if (previous != null) { 18 entryRemoved(false, key, previous, value); 19 } 20 21 //该方法是对总容量的调整:如果超过则删除离header最近的元素,直到小于总容量 22 trimToSize(maxSize); 23 return previous; 24 } 25 26 public void trimToSize(int maxSize) { 27 while (true) { 28 K key; 29 V value; 30 synchronized (this) { 31 if (size < 0 || (map.isEmpty() && size != 0)) { 32 throw new IllegalStateException(getClass().getName() 33 + ".sizeOf() is reporting inconsistent results!"); 34 } 35 36 if (size <= maxSize) { 37 break; 38 } 39 40 //获取最早保存的数据进行删除 41 Map.Entry<K, V> toEvict = map.eldest(); 42 if (toEvict == null) { 43 break; 44 } 45 46 key = toEvict.getKey(); 47 value = toEvict.getValue(); 48 map.remove(key); 49 size -= safeSizeOf(key, value); 50 evictionCount++; 51 } 52 53 entryRemoved(true, key, value, null); 54 } 55 }
二、DiskLruCache:官方认证的硬盘缓存方式,不是android的API,需要下载才能使用。
1、首先是创建过程,该类不能直接创建,需要通过open方法进行创建,
1 /* @param directory 缓存路径, 2 * @param valueCount 每个缓存实体对应的缓存数量,通常是1 3 * @param maxSize 最大缓存容量 4 */ 5 6 //一个用户获取可用的缓存路径的方法,首先是外部存储器,然后是内部存储器 7 public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) 8 9 public File getDiskCacheDir(Context context, String uniqueName) { 10 String cachePath; 11 if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 12 || !Environment.isExternalStorageRemovable()) { 13 cachePath = context.getExternalCacheDir().getPath(); 14 } else { 15 cachePath = context.getCacheDir().getPath(); 16 } 17 return new File(cachePath + File.separator + uniqueName); 18 } 19 20 //获取应用版本号的方法 21 public int getAppVersion(Context context) { 22 try { 23 PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); 24 return info.versionCode; 25 } catch (NameNotFoundException e) { 26 e.printStackTrace(); 27 } 28 return 1; 29 }
2、写入缓存:通过DiskLruCache.Editor来操作的,创建该类使用的是edit(),
1 //参数key将作为该缓存的名字,为将请求url和其对应,通常使用md5对其操作生成唯一字符串 2 public Editor edit(String key) throws IOException { 3 return edit(key, ANY_SEQUENCE_NUMBER); 4 }
创建一个Editor之后就可以通过它生成一个输出流,向缓存文件中进行输出操作。
3、读取缓存:通过DiskLruCache的get()可以获取一个Snapshot对象,该对象中包含着这个缓存文件的输出流,可以对流进行读取获取其中的缓存。
4、其他操作:
①移除缓存:remove()
②获取缓存大小:size()
③通过缓存操作到日志文件(也就是journal文件),通常在Activity的onPause()中调用一次即可。
④清空缓存:delete()
⑤关闭缓存:close(),和open()对应。