ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架。轻量到只有一个java文件(由十几个类精简而来)
仓库地址: https://github.com/yangfuhai/ASimpleCache
普通的字符串、JsonObject、JsonArray、Bitmap、Drawable、序列化的java对象,和 byte数据。
##3、它在android中可以用在哪些场景?
##4、如何使用 ASimpleCache?以下有个小的demo,希望您能喜欢:
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
获取数据
ACache mCache = ACache.get(this);
String value = mCache.getAsString("test_key1");
可以由上个简单的例子可以看出 ACache
是核心类,但是起构造方法是 private 的,我们只能通过 ACache.get()
获得其实例,由此做切入分析
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
对象 private static final int MAX_SIZE = 1000 * 1000 * 50; // 50 mb
private static final int MAX_COUNT = Integer.MAX_VALUE; // 不限制存放数据的数量
继续看ACache 的构造方法
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
是一个缓存管理器, ACache
的内部类,最终的缓存的 put 和 get都由这个类管理
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 都是原子量,不用加锁保证了线程安全,
/**
* 计算 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
,
到这里获取缓存实例工作完成,主要完成了如下工作:
###缓存写数据
/**
* 保存 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);
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
}
移除最不常用的文件
**
* 移除旧的文件
* @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();
}
}
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)即可。