Android开源中国客户端学习 主界面功能分析<2> 缓存功能分析

本文解析了一个应用程序中的缓存实现方式,介绍了如何通过读写文件来缓存数据,并探讨了网络加载与缓存读取的不同场景及流程。同时,还讨论了缓存清理策略和存在的问题。

上传有个哥们说缓存问题,那就先说一下缓存的实现吧,其实 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后会调用:

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 对象中放太多函数甚至要放内存缓存进去,是不是有些不合适?


转载于:https://my.oschina.net/sfshine/blog/139125

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值