上传有个哥们说缓存问题,那就先说一下缓存的实现吧,其实 osc的缓存实现很简单,就是读写文件而已。不曾用到数据库,其实这样 实现起来效果如大家所见 也不能说不好,要说不好感觉不少缓存放到了手机内存,当然这对于今天的手机问题不大 而且很多cache会在刷新的时候被替换,app也有清理缓存功能。
ok接着上面加载新闻list的第五步开始介绍吧
代码
public NewsList getNewsList(int catalog, int pageIndex, boolean isRefresh) throws AppException {
NewsList list = null;
String key = "newslist_"+catalog+"_"+pageIndex+"_"+PAGE_SIZE;
if(isNetworkConnected() && (!isReadDataCache(key) || isRefresh)) {
try{
list = ApiClient.getNewsList(this, catalog, pageIndex, PAGE_SIZE);
if(list != null && pageIndex == 0){
Notice notice = list.getNotice();
list.setNotice(null);
list.setCacheKey(key);
saveObject(list, key);
list.setNotice(notice);
}
}catch(AppException e){
list = (NewsList)readObject(key);
if(list == null)
throw e;
}
} else {
list = (NewsList)readObject(key);
if(list == null)
list = new NewsList();
}
return list;
}
其实这里整体上可以认为分两种情况,(当然我们通过代码知道不是两个分支,流程图如下)
第一种:从网络加载数据:时序图如下:
3 在调用 ApiClient.getNewsList从网络获取list后会调用:
4
list.setCacheKey(key);
saveObject(list, key);
这两句代码 把数据序列化到本地。
key是什么呢?
String key = "newslist_"+catalog+"_"+pageIndex+"_"+PAGE_SIZE;
其实可以理解为含有一些请求信息的唯一的id
setCacheKey是为了给序列化一个key方便以后查找 这个函数在newslist逻辑中没有用到 在后面会看到用处
NewList的整体数据结构如下:
其实很简单,就是实现Serializable 然后使用文件保存下来
saveObject 代码
public boolean saveObject(Serializable ser, String file) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try{
fos = openFileOutput(file, MODE_PRIVATE);
oos = new ObjectOutputStream(fos);
oos.writeObject(ser);
oos.flush();
return true;
}catch(Exception e){
e.printStackTrace();
return false;
}finally{
try {
oos.close();
} catch (Exception e) {}
try {
fos.close();
} catch (Exception e) {}
}
}
文件保存在/data/data/new/net.oschina.app/files中 打开以newslist开头的显示如下
第二种:从缓存读取数据:
第二种情况发生在没有网络或者用缓存不用刷新等情况中时序图:
使用的方法是 原来是根据key读取 缓存在本地的文件然后返回list
list = (NewsList)readObject(key);
if(list == null)
list = new NewsList();
public Serializable readObject(String file){
if(!isExistDataCache(file))
return null;
FileInputStream fis = null;
ObjectInputStream ois = null;
try{
fis = openFileInput(file);
ois = new ObjectInputStream(fis);
return (Serializable)ois.readObject();
}catch(FileNotFoundException e){
}catch(Exception e){
e.printStackTrace();
//反序列化失败 - 删除缓存文件
if(e instanceof InvalidClassException){
File data = getFileStreamPath(file);
data.delete();
}
}finally{
try {
ois.close();
} catch (Exception e) {}
try {
fis.close();
} catch (Exception e) {}
}
return null;
}
这里有个问题,就是isReadDataCache函数
private boolean isReadDataCache(String cachefile)
{
return readObject(cachefile) != null;
}
这样的话 读缓存时就多读取了一次,为什么不读取一次 供判断是否有缓存 和读取使用呢?
ok这是大致的流程
在介绍一下AppContext中关于缓存的其他也许功能
清理缓存
/**
* 清除app缓存
*/
public void clearAppCache()
{
//清除webview缓存
File file = CacheManager.getCacheFileBaseDir();
if (file != null && file.exists() && file.isDirectory()) {
for (File item : file.listFiles()) {
item.delete();
}
file.delete();
}
deleteDatabase("webview.db");
deleteDatabase("webview.db-shm");
deleteDatabase("webview.db-wal");
deleteDatabase("webviewCache.db");
deleteDatabase("webviewCache.db-shm");
deleteDatabase("webviewCache.db-wal");
//清除数据缓存
clearCacheFolder(getFilesDir(),System.currentTimeMillis());
clearCacheFolder(getCacheDir(),System.currentTimeMillis());
//2.2版本才有将应用缓存转移到sd卡的功能
if(isMethodsCompat(android.os.Build.VERSION_CODES.FROYO)){
clearCacheFolder(MethodsCompat.getExternalCacheDir(this),System.currentTimeMillis());
}
//清除编辑器保存的临时内容
Properties props = getProperties();
for(Object key : props.keySet()) {
String _key = key.toString();
if(_key.startsWith("temp"))
removeProperty(_key);
}
}
清理刚才我们保存的缓存文件的方法 其实就是遍历一边 所有文件 然后清理 不明白的是
child.lastModified() < curTime
这句话,感觉在这里没有意义,但是很有可能 作者给缓存添加生命期限的,但是暂时没有发现缓存的生命期限控制
/**
* 清除缓存目录
*
* @param dir
* 目录
* @param numDays
* 当前系统时间
* @return
*/
private int clearCacheFolder(File dir, long curTime) {
int deletedFiles = 0;
if (dir != null && dir.isDirectory()) {
try {
for (File child : dir.listFiles()) {
if (child.isDirectory()) {
deletedFiles += clearCacheFolder(child, curTime);
}
if (child.lastModified() < curTime) {
if (child.delete()) {
deletedFiles++;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return deletedFiles;
}
作者其实也想做内存缓存和 sd卡磁盘缓存(getMemCache,setDiskCache) 结果app中也没有用的这些方法
内存缓存不做可能为了内存 sd卡缓存不做可能为了兼容性吧。
这里在提出一个问题 我们知道application对象对象是应用进程中的一个对象,有自己的任务要做 把application 对象中放太多函数甚至要放内存缓存进去,是不是有些不合适?