ASimpleCache学习笔记
景靖琪
2023-12-01
开源缓存框架ASimpleCache调研学习:
源码网址:https://github.com/yangfuhai/ASimpleCache
二级缓存定义:
当Android端需要获得数据时比如获取网络中的图片,我们首先从内存中查找(按键查找),内存中没有的再从磁盘文件或sqlite中去查找,若磁盘中也没有才通过网络获取;当获得来自网络的数据,就以key-value对的方式先缓存到内存(一级缓存),同时缓存到文件或sqlite中(二级缓存)。注意:内存缓存会造成堆内存泄露,所有一级缓存通常要严格控制缓存的大小,一般控制在系统内存的1/4。
##1、如何使用 ASimpleCache? 如下是github上给出的一个简单例子:
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");
##2,它只有一个类,约800行,可以缓存数据类型有:普通的字符串、JsonObject、JsonArray、Bitmap、Drawable、序列化的java对象,和 byte数据;还可以设置缓存大小和缓存时间。
##3,
####3.1
ACache采用典型的单例实现,初始化使用get()方法,共有5个get()方法可以获取ACache对象,但最终都会调用到public static ACache get(File cacheDir, long max_zise, int 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;
}
这个方法中,根据缓存目录和当前的进程id获取Acache对象,如果没有,则会构造一个ACache对象并保存到mInstanceMap中。Acache为不同进程设置了不同的缓存文件夹。通过mInstanceMap来管理不同进程的缓存实例。
####3.2 如何构造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);
}
构造ACache对象的方法中,会创建缓存的目录,然后调用ACacheManager的构造,初始化了变量mCache。ACacheManager内部简单的实现了LRU,Acache则为ACacheManager提供一层封装,负责对外提供接口。
####3.3 ACacheManager构造方法
private ACacheManager(File cacheDir, long sizeLimit, int countLimit) {
this.cacheDir = cacheDir;
this.sizeLimit = sizeLimit;
this.countLimit = countLimit;
cacheSize = new AtomicLong();
cacheCount = new AtomicInteger();
calculateCacheSizeAndCacheCount();
}
在ACacheManager类中,使用Map<File, Long> lastUsageDates存储文件最近的使用时间,在缓存大小大于sizeLimit,或者缓存数量大于countLimit时,会根据lastUsageDates保存的时间,删除最近最少使用的缓存文件。
##4 mCache.put("test_key1", "test value");究竟做了什么工作?
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);
}
}
####4.1 mCache.newFile(key); 首先通过ACacheManager对象mCache在默认缓存目录或者自己设定的缓存目录下,以key的hashcode为名,创建缓存文件。
private File newFile(String key) {
return new File(cacheDir, key.hashCode() + "");
}
####4.2 把put方法传入的value值写入刚刚创建的缓存文件中。
####4.3 在finally里面调用mCache.put(file);,更新ACacheManager中的cacheCount,cacheSize以及lastUsageDates中保存的文件最新修改时间。
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);
}
##5 如何从缓存获取数据的?
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);
}
}
####5.1 首先调用ACacheManager对象mCache.get(key);,返回file对象,同时更新file的最新修改时间,以便缓存满时清除缓存。
private File get(String key) {
File file = newFile(key);
Long currentTime = System.currentTimeMillis();
file.setLastModified(currentTime);
lastUsageDates.put(file, currentTime);
return file;
}
####5.2 接着有个非空判断,判断文件是否存在,个人认为似乎没用,因为mCache.get(key)中,直接new了一个file。
####5.3 然后就是从缓存文件中读取数据,判断若没有过期,则返回value;否则返回null,并且在finally中删除过期的缓存文件。
##6 可以设置缓存有效时间是如何实现的?
####6.1 在put的时候,将当前的时间和缓存的有效时间以一定的格式拼接在要保存的value的前面。
public void put(String key, String value, int saveTime) {
put(key, Utils.newStringWithDateInfo(saveTime, value));
}
####6.2 在get的时候,去掉时间信息,返回保存的原始value。
if (!Utils.isDue(readString)) {
return Utils.clearDateInfo(readString);
......
##7 网上很多资料描述,ASimpleCache将数据同时存入一级缓存(内存Map)和二级缓存(文件)中。但是看了源码,发现ASimpleCache实现的只是二级缓存,其实并没有保存到内存中。
##8 自己网上搜索了些资料,整理了一下,目前实现了一个能同时存入一级缓存和二级缓存的ACache。