采用缓存,可以进一步大大缓解数据交互的压力,又能提供一定的离线浏览。下边我简略列举一下缓存管理的适用环境:

1. 提供网络服务的应用

2. 数据更新不需要实时更新,哪怕是3-5分钟的延迟也是可以采用缓存机制。

3. 缓存的过期时间是可以接受的(类似网易的新闻阅读,支持离线离线阅读)

这样所带来的好处:

1. 减小服务器的压力

2. 提高客户端的响应速度(本地数据提取嘛)

3. 一定程度上支持离线浏览(可以参考网易的那个新闻应用,个人感觉离线阅读做得非常棒。)

一、缓存管理的方法

缓存管理的原理很简:通过时间的设置来判断是否读取缓存还是重新下载;断网下就没什么好说的,直接去缓存即可。如图:

里面会有一些细节的处理,后面会详细阐述。基于这个原理,目前个人用过的两种比较常见的缓存管理方法是:数据库和文件(txt)。

二、数据库(SQLite)缓存方式

这种方法是在下载完数据文件后,把文件的相关信息如url,路经,下载时间,过期时间等存放到数据库,当然我个人建议把url作为唯一的标识。下次下载的时候根据url先从数据库中查询,如果查询到当前时间并未过期,就根据路径读取本地文件,从而实现缓存的效果。

从实现上我们可以看到这种方法可以灵活存放文件的属性,进而提供了很大的扩展性,可以为其它的功能提供一定的支持。

从操作上需要创建数据库,每次查询数据库,如果过期还需要更新数据库,清理缓存的时候还需要删除数据库数据,稍显麻烦,而数据库操作不当又容易出现一系列的性能,ANR问题,指针错误问题,实现的时候要谨慎,具体作的话,但也只是增加一个工具类或方法的事情。

还有一个问题,缓存的数据库是存放在/data/data/<package>/databases/目录下,是占用内存空间的,如果缓存累计,容易浪费内存,需要及时清理缓存。

当然这种方法从目前一些应用的实用上看,我没有发现什么问题,估计使用的量还比较少吧。

本文本人不太喜欢数据库,原因操作麻烦,尤其是要自己写建表那些语句,你懂的。我侧重文件缓存方式。

三、文件缓存方式

这种方法,使用File.lastModified()方法得到文件的最后修改时间,与当前时间判断是否过期,从而实现缓存效果。

实现上只能使用这一个属性,没有为其它的功能提供技术支持的可能。操作上倒是简单,比较时间即可,而且取的数据也就是文件里的JSON数据而已。本身处理也不容易带来其它问题,代价低廉。

四、文件法缓存方式的两点说明

1. 不同类型的文件的缓存时间不一样。

笼统的说,不变文件的缓存时间是永久,变化文件的缓存时间是最大忍受不变时间。说白点,图片文件内容是不变的,一般存在SD卡上直到被清理,我们是可以永远读取缓存的。配置文件内容是可能更新的,需要设置一个可接受的缓存时间。

2. 不同环境下的缓存时间标准不一样。

无网络环境下,我们只能读取缓存文件,为了应用有东西显示,没有什么过期之说了。

WiFi网络环境下,缓存时间可以设置短一点,一是网速较快,而是流量不要钱。

3G流量环境下,缓存时间可以设置长一点,节省流量,就是节省金钱,而且用户体验也更好。

GPS就别说更新什么的,已经够慢的了。缓存时间能多长就多长把。

当然,作为一款好的应用,不会死定一种情况,针对于不同网络变换不同形式的缓存功能是必须有的。而且这个时间根据自己的实际情况来设置:数据的更新频率,数据的重要性等。

五、何时刷新

开发者一方面希望尽量读取缓存,用户一方面希望实时刷新,但是响应速度越快越好,流量消耗越少越好(关于这块,的确开发中我没怎么想到,毕竟接口就是这么多,现在公司的产品几乎点一下就访问一下,而且还有些鸡肋多余的功能。慢慢修改哈哈),是一个矛盾。

其实何时刷新我也不知道,这里我提供两点建议:

1. 数据的最长多长时间不变,对应用无大的影响。

比如,你的数据更新时间为4小时,则缓存时间设置为1~2小时比较合适。也就是更新时间/缓存时间=2,但用户个人修改、网站编辑人员等一些人为的更新就另说。一天用户总会看到更新,即便有延迟也好,视你产品的用途了;如果你觉得你是资讯类应用,再减少,2~4小时,如果你觉得数据比较重要或者比较受欢迎,用户会经常把玩,再减少,1~2小时,依次类推。

当然类似这个界面的数据我认为更新时间能多长就多长了,尽可能长。如果你拿后边那个有多少数据会变动来搪塞。我会告诉你:这个只是一个引导性的界面,你有多少款游戏跟用户半毛钱关系都没有,10亿也跟他没关,他只要确定这里能找到他要找的 汤姆猫 就行。否则你又失去了一个用户。

2. 提供刷新按钮。

必要时候或最保险的方法使在相关界面提供一个刷新按钮,或者当下流行的下拉列表刷新方式。为缓存,为加载失败提供一次重新来过的机会。毕竟喝骨头汤的时候,我也不介意碗旁多双筷子。

总而言之,一切用户至上,为了更好的用户体验,方法也会层出不穷。期待更好的办法

关键代码:

  1. package com.hulutan.gamestore.cache;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import android.os.Environment;
  5. import android.util.Log;
  6. import com.hulutan.gamestore.Constants;
  7. import com.hulutan.gamestore.GameStoreApplication;
  8. import com.hulutan.gamestore.util.EncryptUtils;
  9. import com.hulutan.gamestore.util.FileUtils;
  10. import com.hulutan.gamestore.util.NetworkUtils;
  11. import com.hulutan.gamestore.util.NetworkUtils.NetworkState;
  12. import com.hulutan.gamestore.util.StringUtils;
  13. /**
  14. * 缓存工具类
  15. * @author naibo-liao
  16. * @时间: 2013-1-4下午02:30:52
  17. */
  18. public class ConfigCacheUtil {
  19. private static final String TAG=ConfigCacheUtil.class.getName();
  20. /** 1秒超时时间 */
  21. public static final int CONFIG_CACHE_SHORT_TIMEOUT=1000 * 60 * 5; // 5 分钟
  22. /** 5分钟超时时间 */
  23. public static final int CONFIG_CACHE_MEDIUM_TIMEOUT=1000 * 3600 * 2; // 2小时
  24. /** 中长缓存时间 */
  25. public static final int CONFIG_CACHE_ML_TIMEOUT=1000 * 60 * 60 * 24 * 1; // 1天
  26. /** 最大缓存时间 */
  27. public static final int CONFIG_CACHE_MAX_TIMEOUT=1000 * 60 * 60 * 24 * 7; // 7天
  28. /**
  29. * CONFIG_CACHE_MODEL_LONG : 长时间(7天)缓存模式 <br>
  30. * CONFIG_CACHE_MODEL_ML : 中长时间(12小时)缓存模式<br>
  31. * CONFIG_CACHE_MODEL_MEDIUM: 中等时间(2小时)缓存模式 <br>
  32. * CONFIG_CACHE_MODEL_SHORT : 短时间(5分钟)缓存模式
  33. */
  34. public enum ConfigCacheModel {
  35. CONFIG_CACHE_MODEL_SHORT, CONFIG_CACHE_MODEL_MEDIUM, CONFIG_CACHE_MODEL_ML, CONFIG_CACHE_MODEL_LONG;
  36. }
  37. /**
  38. * 获取缓存
  39. * @param url 访问网络的URL
  40. * @return 缓存数据
  41. */
  42. public static String getUrlCache(String url, ConfigCacheModel model) {
  43. if(url == null) {
  44. return null;
  45. }
  46. String result=null;
  47. String path=Constants.ENVIROMENT_DIR_CACHE + StringUtils.replaceUrlWithPlus(EncryptUtils.encryptToMD5(url));
  48. File file=new File(path);
  49. if(file.exists() && file.isFile()) {
  50. long expiredTime=System.currentTimeMillis() - file.lastModified();
  51. Log.d(TAG, file.getAbsolutePath() + " expiredTime:" + expiredTime / 60000 + "min");
  52. // 1。如果系统时间是不正确的
  53. // 2。当网络是无效的,你只能读缓存
  54. if(NetworkUtils.getNetworkState(GameStoreApplication.getInstance().getContext()) != NetworkState.NETWORN_NONE) {
  55. if(expiredTime < 0) {
  56. return null;
  57. }
  58. if(model == ConfigCacheModel.CONFIG_CACHE_MODEL_SHORT) {
  59. if(expiredTime > CONFIG_CACHE_SHORT_TIMEOUT) {
  60. return null;
  61. }
  62. } else if(model == ConfigCacheModel.CONFIG_CACHE_MODEL_MEDIUM) {
  63. if(expiredTime > CONFIG_CACHE_MEDIUM_TIMEOUT) {
  64. return null;
  65. }
  66. } else if(model == ConfigCacheModel.CONFIG_CACHE_MODEL_ML) {
  67. if(expiredTime > CONFIG_CACHE_ML_TIMEOUT) {
  68. return null;
  69. }
  70. } else if(model == ConfigCacheModel.CONFIG_CACHE_MODEL_LONG) {
  71. if(expiredTime > CONFIG_CACHE_MEDIUM_TIMEOUT) {
  72. return null;
  73. }
  74. } else {
  75. if(expiredTime > CONFIG_CACHE_MAX_TIMEOUT) {
  76. return null;
  77. }
  78. }
  79. }
  80. try {
  81. result=FileUtils.readTextFile(file);
  82. } catch(IOException e) {
  83. e.printStackTrace();
  84. }
  85. }
  86. return result;
  87. }
  88. /**
  89. * 设置缓存
  90. * @param data
  91. * @param url
  92. */
  93. public static void setUrlCache(String data, String url) {
  94. if(Constants.ENVIROMENT_DIR_CACHE == null) {
  95. return;
  96. }
  97. File dir=new File(Constants.ENVIROMENT_DIR_CACHE);
  98. if(!dir.exists() && Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
  99. dir.mkdirs();
  100. }
  101. File file=new File(Constants.ENVIROMENT_DIR_CACHE + StringUtils.replaceUrlWithPlus(EncryptUtils.encryptToMD5(url)));
  102. try {
  103. // 创建缓存数据到磁盘,就是创建文件
  104. FileUtils.writeTextFile(file, data);
  105. } catch(IOException e) {
  106. Log.d(TAG, "write " + file.getAbsolutePath() + " data failed!");
  107. e.printStackTrace();
  108. } catch(Exception e) {
  109. e.printStackTrace();
  110. }
  111. }
  112. /**
  113. * 删除历史缓存文件
  114. * @param cacheFile
  115. */
  116. public static void clearCache(File cacheFile) {
  117. if(cacheFile == null) {
  118. if(Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
  119. try {
  120. File cacheDir=new File(Environment.getExternalStorageDirectory().getPath() + "/hulutan/cache/");
  121. if(cacheDir.exists()) {
  122. clearCache(cacheDir);
  123. }
  124. } catch(Exception e) {
  125. e.printStackTrace();
  126. }
  127. }
  128. } else if(cacheFile.isFile()) {
  129. cacheFile.delete();
  130. } else if(cacheFile.isDirectory()) {
  131. File[] childFiles=cacheFile.listFiles();
  132. for(int i=0; i < childFiles.length; i++) {
  133. clearCache(childFiles[i]);
  134. }
  135. }
  136. }
  137. }

获取缓存:

  1. String cacheConfigString=ConfigCacheUtil.getUrlCache(Net.API_HELP, ConfigCacheModel.CONFIG_CACHE_MODEL_LONG);
  2. if(cacheConfigString != null) {
  3. //do something
  4. }

设置缓存:

  1. ConfigCacheUtil.setUrlCache(data, Net.API_HELP);

补充  FileUtils 文件

    1. /**
    2. * 文件处理工具类
    3. *
    4. * @author naibo-liao
    5. * @时间: 2013-1-4下午03:13:08
    6. */
    7. public class FileUtils {
    8. public static final long B = 1;
    9. public static final long KB = B * 1024;
    10. public static final long MB = KB * 1024;
    11. public static final long GB = MB * 1024;
    12. private static final int BUFFER = 8192;
    13. /**
    14. * 格式化文件大小<b> 带有单位
    15. *
    16. * @param size
    17. * @return
    18. */
    19. public static String formatFileSize(long size) {
    20. StringBuilder sb = new StringBuilder();
    21. String u = null;
    22. double tmpSize = 0;
    23. if (size < KB) {
    24. sb.append(size).append("B");
    25. return sb.toString();
    26. } else if (size < MB) {
    27. tmpSize = getSize(size, KB);
    28. u = "KB";
    29. } else if (size < GB) {
    30. tmpSize = getSize(size, MB);
    31. u = "MB";
    32. } else {
    33. tmpSize = getSize(size, GB);
    34. u = "GB";
    35. }
    36. return sb.append(twodot(tmpSize)).append(u).toString();
    37. }
    38. /**
    39. * 保留两位小数
    40. *
    41. * @param d
    42. * @return
    43. */
    44. public static String twodot(double d) {
    45. return String.format("%.2f", d);
    46. }
    47. public static double getSize(long size, long u) {
    48. return (double) size / (double) u;
    49. }
    50. /**
    51. * sd卡挂载且可用
    52. *
    53. * @return
    54. */
    55. public static boolean isSdCardMounted() {
    56. return android.os.Environment.getExternalStorageState().equals(
    57. android.os.Environment.MEDIA_MOUNTED);
    58. }
    59. /**
    60. * 递归创建文件目录
    61. *
    62. * @param path
    63. * */
    64. public static void CreateDir(String path) {
    65. if (!isSdCardMounted())
    66. return;
    67. File file = new File(path);
    68. if (!file.exists()) {
    69. try {
    70. file.mkdirs();
    71. } catch (Exception e) {
    72. Log.e("hulutan", "error on creat dirs:" + e.getStackTrace());
    73. }
    74. }
    75. }
    76. /**
    77. * 读取文件
    78. *
    79. * @param file
    80. * @return
    81. * @throws IOException
    82. */
    83. public static String readTextFile(File file) throws IOException {
    84. String text = null;
    85. InputStream is = null;
    86. try {
    87. is = new FileInputStream(file);
    88. text = readTextInputStream(is);;
    89. } finally {
    90. if (is != null) {
    91. is.close();
    92. }
    93. }
    94. return text;
    95. }
    96. /**
    97. * 从流中读取文件
    98. *
    99. * @param is
    100. * @return
    101. * @throws IOException
    102. */
    103. public static String readTextInputStream(InputStream is) throws IOException {
    104. StringBuffer strbuffer = new StringBuffer();
    105. String line;
    106. BufferedReader reader = null;
    107. try {
    108. reader = new BufferedReader(new InputStreamReader(is));
    109. while ((line = reader.readLine()) != null) {
    110. strbuffer.append(line).append("\r\n");
    111. }
    112. } finally {
    113. if (reader != null) {
    114. reader.close();
    115. }
    116. }
    117. return strbuffer.toString();
    118. }
    119. /**
    120. * 将文本内容写入文件
    121. *
    122. * @param file
    123. * @param str
    124. * @throws IOException
    125. */
    126. public static void writeTextFile(File file, String str) throws IOException {
    127. DataOutputStream out = null;
    128. try {
    129. out = new DataOutputStream(new FileOutputStream(file));
    130. out.write(str.getBytes());
    131. } finally {
    132. if (out != null) {
    133. out.close();
    134. }
    135. }
    136. }
    137. /**
    138. * 将Bitmap保存本地JPG图片
    139. * @param url
    140. * @return
    141. * @throws IOException
    142. */
    143. public static String saveBitmap2File(String url) throws IOException {
    144. BufferedInputStream inBuff = null;
    145. BufferedOutputStream outBuff = null;
    146. SimpleDateFormat sf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
    147. String timeStamp = sf.format(new Date());
    148. File targetFile = new File(Constants.ENVIROMENT_DIR_SAVE, timeStamp
    149. + ".jpg");
    150. File oldfile = ImageLoader.getInstance().getDiscCache().get(url);
    151. try {
    152. inBuff = new BufferedInputStream(new FileInputStream(oldfile));
    153. outBuff = new BufferedOutputStream(new FileOutputStream(targetFile));
    154. byte[] buffer = new byte[BUFFER];
    155. int length;
    156. while ((length = inBuff.read(buffer)) != -1) {
    157. outBuff.write(buffer, 0, length);
    158. }
    159. outBuff.flush();
    160. return targetFile.getPath();
    161. } catch (Exception e) {
    162. } finally {
    163. if (inBuff != null) {
    164. inBuff.close();
    165. }
    166. if (outBuff != null) {
    167. outBuff.close();
    168. }
    169. }
    170. return targetFile.getPath();
    171. }
    172. /**
    173. * 读取表情配置文件
    174. *
    175. * @param context
    176. * @return
    177. */
    178. public static List<String> getEmojiFile(Context context) {
    179. try {
    180. List<String> list = new ArrayList<String>();
    181. InputStream in = context.getResources().getAssets().open("emoji");// 文件名字为rose.txt
    182. BufferedReader br = new BufferedReader(new InputStreamReader(in,
    183. "UTF-8"));
    184. String str = null;
    185. while ((str = br.readLine()) != null) {
    186. list.add(str);
    187. }
    188. return list;
    189. } catch (IOException e) {
    190. e.printStackTrace();
    191. }
    192. return null;
    193. }
    194. /**
    195. * 获取一个文件夹大小
    196. *
    197. * @param f
    198. * @return
    199. * @throws Exception
    200. */
    201. public static long getFileSize(File f) {
    202. long size = 0;
    203. File flist[] = f.listFiles();
    204. for (int i = 0; i < flist.length; i++) {
    205. if (flist[i].isDirectory()) {
    206. size = size + getFileSize(flist[i]);
    207. } else {
    208. size = size + flist[i].length();
    209. }
    210. }
    211. return size;
    212. }
    213. /**
    214. * 删除文件
    215. *
    216. * @param file
    217. */
    218. public static void deleteFile(File file) {
    219. if (file.exists()) { // 判断文件是否存在
    220. if (file.isFile()) { // 判断是否是文件
    221. file.delete(); // delete()方法 你应该知道 是删除的意思;
    222. } else if (file.isDirectory()) { // 否则如果它是一个目录
    223. File files[] = file.listFiles(); // 声明目录下所有的文件 files[];
    224. for (int i = 0; i < files.length; i++) { // 遍历目录下所有的文件
    225. deleteFile(files[i]); // 把每个文件 用这个方法进行迭代
    226. }
    227. }
    228. file.delete();
    229. }
    230. }
    231. }

app缓存设计-文件缓存的更多相关文章

  1. [Android]异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3574131.html  这个可以实现ImageView异步加载 ...

  2. PHP程序缓存之文件缓存处理方式

    PHP程序缓存之文件缓存处理方式在开发程序过程中,缓存的设置大大提升程序效率,减小数据库负载.基本配置缓存和常规配置缓存 基本配置缓存在项目开发中类似这样子的格式: 文件:config.php $CF ...

  3. thinkphp3.2配置redis缓存和文件缓存

    如果把一些常用但又不容易变的数据存缓存,而不是每次查数据库,这样能很大减轻数据库压力 最近由于项目需要,就尝试了一把redis,但是后面又用了tp3.2的文件缓存,直接进入主题: 在config.ph ...

  4. .net 缓存之文件缓存依赖

    CaCheHelp 类中代码如下: #region 根据键从缓存中读取保持的数据 /// <summary> /// 根据键从缓存中读取保持的数据 /// </summary> ...

  5. Cache缓存设计

    缓存的适用场景: 缓存的目的是提高访问速度,减少不必要的开销,提高性能.那什么样的场景适用于缓存呢.试想一个多项式的计算是一个CPU bound的操作,如果频繁调用同一个多项式的结果.显然缓存结果是一 ...

  6. C#高性能大容量SOCKET并发(四):缓存设计

    原文:C#高性能大容量SOCKET并发(四):缓存设计 在编写服务端大并发的应用程序,需要非常注意缓存设计,缓存的设计是一个折衷的结果,需要通过并发测试反复验证.有很多服务程序是在启动时申请足够的内存 ...

  7. iOS自定义弹出视图、收音机APP、图片涂鸦、加载刷新、文件缓存等源码

    iOS精选源码 一款优秀的 聆听夜空FM 源码 zhPopupController 简单快捷弹出自定义视图 WHStoryMaker搭建美图(贴纸,涂鸦,文字,滤镜) iOS cell高度自适应 有加 ...

  8. 大型web系统数据缓存设计

    1. 前言 在高访问量的web系统中,缓存几乎是离不开的:但是一个适当.高效的缓存方案设计却并不容易:所以接下来将讨论一下应用系统缓存的设计方面应该注意哪些东西,包括缓存的选型.常见缓存系统的特点和数 ...

  9. web app 禁用手机浏览器缓存方法

    开发过web app的同学,特别是前端人员,都碰到这烦人的事情,JS或CSS代码改变,可手机浏览器怎么刷新都不更新,手机浏览器的缓存特别恶劣. 所以今天贴个方法解决这问题.记得,本地调试的时候贴上,上 ...

随机推荐

  1. fpm来制作rpm包

    转自 http://blog.halfss.com/blog/2013/02/26/fpmbao-guan-li/ 另查看 http://my.oschina.net/lxcong/blog/1438 ...

  2. OSPF理解

    from http://kingdee.blog.51cto.com/98119/27310STP,PIM,OSPF,长的好像(*_*)可以把整个网络(一个自治系统AS)看成一个王国,这个王国可以分成 ...

  3. 【转】轻量级分布式 RPC 框架

    第一步:编写服务接口 第二步:编写服务接口的实现类 第三步:配置服务端 第四步:启动服务器并发布服务 第五步:实现服务注册 第六步:实现 RPC 服务器 第七步:配置客户端 第八步:实现服务发现 第九 ...

  4. Velocity(1)——注释

    Velocity的单行注释,使用## 多行注释使用#* cooments *#

  5. jquery获取自身元素的html

    在开发过程中,jQuery.html() 是获取当前节点下的html代码,并不包含当前节点本身的代码,然而我们有时候的确需要,可以通过jQuery.prop("outerHTML" ...

  6. Lintcode: Segment Tree Modify

    For a Maximum Segment Tree, which each node has an extra value max to store the maximum value in thi ...

  7. zoj The 12th Zhejiang Provincial Collegiate Programming Contest Convert QWERTY to Dvorak

    http://acm.zju.edu.cn/onlinejudge/showContestProblem.do?problemId=5502  The 12th Zhejiang Provincial ...

  8. java中的断言

     断言:也就是所谓的assertion,是jdk1.4后加入的新功能. 它主要使用在代码开发和测试时期,用于对某些关键数据的判断,如果这个关键数据不是你程序所预期的数据,程序就提出警告或退出. 当软件 ...

  9. ios pyudaren

    ed2k://|file|%E9%A1%B9%E7%9B%AE%E6%8D%95%E9%B1%BC%E8%BE%BE%E4%BA%BA01.rmvb|67044010|9e013987298d7900 ...

  10. ansible自动化运维工具的安装与使用

    运行环境 centOS6.6 ansible ansible的功能还是比较多的,博主只用它在集群上进行批量部署软件和维护的功能,其他不多做研究,有需要的话这篇文章会慢慢补充. ansible特点 轻量 ...