ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架。轻量到只有一个java文件(由十几个类精简而来)
仓库地址:https://github.com/yangfuhai/ASimpleCache
官方介绍
1、它可以缓存什么东西?
普通的字符串、JsonObject、JsonArray、Bitmap、Drawable、序列化的java对象,和 byte数据。
2、它有什么特色?
特色主要是:
1:轻,轻到只有一个JAVA文件。
2:可配置,可以配置缓存路径,缓存大小,缓存数量等。
3:可以设置缓存超时时间,缓存超时自动失效,并被删除。
4:支持多进程。
##3、它在android中可以用在哪些场景?
1、替换SharePreference当做配置文件
2、可以缓存网络请求数据,比如oschina的android客户端可以缓存http请求的新闻内容,缓存时间假设为1个小时,超时后自动失效,让客户端重新请求新的数据,减少客户端流量,同时减少服务器并发量。
3、您来说…
##4、如何使用 ASimpleCache?
以下有个小的demo,希望您能喜欢:
1 2 3 4 | ACache mCache = ACache.get(this); mCache.put("test_key1", "test value"); mCache.put("test_key2", "test value", 10);//保存10秒,如果超过10秒去获取这个key,将为null mCache.put("test_key3", "test value", 2 * ACache.TIME_DAY);//保存两天,如果超过两天去获取这个key,将为null |
获取数据
1 2 | ACache mCache = ACache.get(this); String value = mCache.getAsString("test_key1"); |
分析
ACache 构造
可以由上个简单的例子可以看出 ACache
是核心类,但是起构造方法是 private 的,我们只能通过ACache.get()
获得其实例,由此做切入分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public static ACache get(Context ctx) { return get(ctx, "ACache"); } public static ACache get(Context ctx, String cacheName) { File f = new File(ctx.getCacheDir(), cacheName); return get(f, MAX_SIZE, MAX_COUNT); } public static ACache get(File cacheDir) { return get(cacheDir, MAX_SIZE, MAX_COUNT); } public static ACache get(Context ctx, long max_zise, int max_count) { File f = new File(ctx.getCacheDir(), "ACache"); return get(f, max_zise, max_count); } public static ACache get(File cacheDir, long max_zise, int max_count) { ACache manager = mInstanceMap.get(cacheDir.getAbsoluteFile() + myPid()); if (manager == null) { manager = new ACache(cacheDir, max_zise, max_count); mInstanceMap.put(cacheDir.getAbsolutePath() + myPid(), manager); } return manager; } |
mCache = ACache.get(this);
ACache get(Context ctx, String cacheName)
,在/data/data/app-package-name/cache/ACache
创建了缓存目录ACache get(File cacheDir, long max_zise, int max_count)
,后两个参数在未传入默认值,在初次运行时manager ==null
,最终会调用到ACache
的 private 的构造方法,并且将结果放入mInstanceMap
中,key 值为路径+Pid,value 为实例化的ACache
对象
1 2 | private static final int MAX_SIZE = 1000 * 1000 * 50; // 50 mb private static final int MAX_COUNT = Integer.MAX_VALUE; // 不限制存放数据的数量 |
继续看ACache 的构造方法
1 2 3 4 5 6 | private ACache(File cacheDir, long max_size, int max_count) { if (!cacheDir.exists() && !cacheDir.mkdirs()) { throw new RuntimeException("can't make dirs in " + cacheDir.getAbsolutePath()); } mCache = new ACacheManager(cacheDir, max_size, max_count); } |
如果目录文件不存在会抛出异常,同时最终将全局变量 ACacheManager
初始化。
ACacheManager
ACacheManager
是一个缓存管理器,ACache
的内部类,最终的缓存的 put 和 get都由这个类管理
1 2 3 4 5 6 7 8 | private ACacheManager(File cacheDir, long sizeLimit, int countLimit) { this.cacheDir = cacheDir; this.sizeLimit = sizeLimit; this.countLimit = countLimit; cacheSize = new AtomicLong(); cacheCount = new AtomicInteger(); calculateCacheSizeAndCacheCount(); } |
cacheSize,cacheCount 都是原子量,不用加锁保证了线程安全,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /** * 计算 cacheSize和cacheCount */ private void calculateCacheSizeAndCacheCount() { new Thread(new Runnable() { @Override public void run() { int size = 0; int count = 0; File[] cachedFiles = cacheDir.listFiles(); if (cachedFiles != null) { for (File cachedFile : cachedFiles) { //遍历 cacheDir 中的文件,并计算大小和数量 size += calculateSize(cachedFile); count += 1; //lastUsageDates Map 中保存了<File,最后修改时间> lastUsageDates.put(cachedFile, cachedFile.lastModified()); } cacheSize.set(size); cacheCount.set(count); } } }).start(); } |
在 calculateCacheSizeAndCacheCount
,开了一个线程计算cacheSize
和cacheCount
,
到这里获取缓存实例工作完成,主要完成了如下工作:
新建了缓存目录
通过ACache构造方法构造新实例,并且将该实例引用插入mInstanceMap
实例化ACacheManager,计算cacheSize和cacheCount
###缓存写数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | /** * 保存 String数据 到 缓存中 * * @param key * 保存的key * @param value * 保存的String数据 * @param saveTime * 保存的时间,单位:秒 */ public void put(String key, String value, int saveTime) { put(key, Utils.newStringWithDateInfo(saveTime, value)); } /** * 保存 String数据 到 缓存中 * * @param key * 保存的key * @param value * 保存的String数据 */ public void put(String key, String value) { //新建文件,每一个 key 对应一个文件 File file = mCache.newFile(key); BufferedWriter out = null; try { //将数据保存至文件 out = new BufferedWriter(new FileWriter(file), 1024); out.write(value); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } //将文件加人mCache mCache.put(file); } } ------ private File newFile(String key) { return new File(cacheDir, key.hashCode() + ""); //新建文件,文件名为key的整型哈希码 } |
put(String key, String value, int saveTime)
第三个是缓存的有效时间
Utils.newStringWithDateInfo(saveTime, value)
会将time 和 value 整合成一个 String
在看看mCache.put(file);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | private void put(File file) { int curCacheCount = cacheCount.get(); //检查文件个数是不是超过显示 while (curCacheCount + 1 > countLimit) { long freedSize = removeNext();//移除旧的文件,返回文件大小 cacheSize.addAndGet(-freedSize);//更新cacheSize curCacheCount = cacheCount.addAndGet(-1);//更新curCacheCount } cacheCount.addAndGet(1); long valueSize = calculateSize(file); long curCacheSize = cacheSize.get(); while (curCacheSize + valueSize > sizeLimit) { //检查缓存大小是不是超过显示 long freedSize = removeNext(); curCacheSize = cacheSize.addAndGet(-freedSize); } cacheSize.addAndGet(valueSize); Long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime);//设置文件最后修改时间 //lastUsageDates Map 中保存了<File,最后修改时间> lastUsageDates.put(file, currentTime); //更新 map } |
移除最不常用的文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ** * 移除旧的文件 * @return */ private long removeNext() { if (lastUsageDates.isEmpty()) { return 0; } Long oldestUsage = null; File mostLongUsedFile = null; Set<Entry<File, Long>> entries = lastUsageDates.entrySet();//获得 Entry<K,V> 的集合 synchronized (lastUsageDates) { for (Entry<File, Long> entry : entries) { //找出最久没有使用的 if (mostLongUsedFile == null) { mostLongUsedFile = entry.getKey(); oldestUsage = entry.getValue(); } else { Long lastValueUsage = entry.getValue(); if (lastValueUsage < oldestUsage) { //数值越大,时间值越大,表示越近使用的 oldestUsage = lastValueUsage; mostLongUsedFile = entry.getKey(); } } } } long fileSize = calculateSize(mostLongUsedFile); if (mostLongUsedFile.delete()) { lastUsageDates.remove(mostLongUsedFile); } return fileSize; } private long calculateSize(File file) { return file.length(); } } |
缓存读数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | public String getAsString(String key) { File file = mCache.get(key); //获取文件 if (!file.exists()) return null; boolean removeFile = false; BufferedReader in = null; try { in = new BufferedReader(new FileReader(file)); String readString = ""; String currentLine; while ((currentLine = in.readLine()) != null) { readString += currentLine; } if (!Utils.isDue(readString)) { //String数据未到期 return Utils.clearDateInfo(readString);//去除时间信息的字符串内容 } else { removeFile = true; //数据到期了则删除缓存 return null; } } catch (IOException e) { e.printStackTrace(); return null; } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (removeFile) remove(key); } } ``` ```java /** * 判断缓存的String数据是否到期 * * @param str * @return true:到期了 false:还没有到期 */ private static boolean isDue(String str) { return isDue(str.getBytes()); } /** * 判断缓存的byte数据是否到期 * * @param data * @return true:到期了 false:还没有到期 */ private static boolean isDue(byte[] data) { String[] strs = getDateInfoFromDate(data);//分别拿出前面的时间信息,和实际的数据 if (strs != null && strs.length == 2) { String saveTimeStr = strs[0]; while (saveTimeStr.startsWith("0")) { //去零 saveTimeStr = saveTimeStr.substring(1, saveTimeStr.length()); } long saveTime = Long.valueOf(saveTimeStr); long deleteAfter = Long.valueOf(strs[1]); if (System.currentTimeMillis() > saveTime + deleteAfter * 1000) { //到期了 return true; } } return false; } |
JsonObject、JsonArray、Bitmap、Drawable、序列化的存入缓存都是转化为字符串/byte格式,再调用函数put(String key, String value)即可。
共同学习,写下你的评论
评论加载中...
作者其他优质文章