ASimpleCache 是一个为Android 制定的 轻量级的 开源缓存框架。轻量到只有一个Java 文件(由十几个类精简而来)
官方介绍
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 ); mCache.put("test_key3" , "test value" , 2 * ACache.TIME_DAY);
获取数据
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 ; 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) { size += calculateSize(cachedFile); count += 1 ; 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) { 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.put(file); } } ------ private File newFile (String key) { return new File(cacheDir, key.hashCode() + "" ); }
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); curCacheCount = cacheCount.addAndGet(-1 ); } 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.put(file, currentTime); }
移除最不常用的文件
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(); 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)) { 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)即可。