原文地址:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1209/2136.html

离线缓存就是在网络畅通的情况下将从服务器收到的数据保存到本地,当网络断开之后直接读取本地文件中的数据。

将网络数据保存到本地:

你可以自己写一个保存数据成本地文件的方法,保存在android系统的任意目录(当然是有权限的才行),但是在这种情况下使用Context的openFileOutput方法最简便也最符合我们的场景,下面的saveObject方法演示了如何用openFileOutput将数据保存在本地的一个文件中:

saveObject

 public static boolean saveObject(Serializable ser, String file) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = AppContext.getInstance().openFileOutput(file, AppContext.getInstance().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) {
}
}
}

openFileOutput可以直接获得一个和应用关联的文件路径(在/data/data/<package name>/files下面),然后使用java io中的ObjectOutputStream将序列化的对象写入(writeObject)到得到的文件中,你可以看到上面的实现过程有两个关键方法:openFileOutputwriteObject以及调用它们的两个关键对象ContextObjectOutputStream。关于序列化可以参看这篇文章:Java对象的序列化和反序列化实践

这是将一个序列化的对象保存在本地,跟我们的离线缓存保存网络数据有什么关系呢?

有关系,因为网上获取的数据大多可以转换成String类型的字符串,现在服务端返回的数据一般是json格式的字符串。而String类型的字符串其实就是可序列化的对象。下面是一个服务器返回json数据的例子(其实就是jcodecraeer):

 {"url":"http://jcodecraeer.com/uploads/soft/android/CodeBox.apk","versionCode":"7","updateMessage":"增加离线缓存,分类筛选功能修正了版本兼容性问题 "}

用上面的saveObject方法我们可 以将数据保存在本地,为了能够取出这个文件我们必须想好如何为这个保存的文件命名,如果是单纯的一篇文章的数据,我们可以直接将文件名命名为这篇文章的 id,因为id是唯一的,为了尽可能的不和其他数据发生冲突,你还可以在这个id之前加一个前缀,比如这篇文章是java栏目下的我们可以这样 arc_java_id。如果是文章列表我们可以这样命名:文章类别_分页页码,总之命名的原则是能和其他离线数据区别,有唯一性。为什么不用url作为 文件名呢?url肯定是唯一的,但是url不一定符合文件的命名规范。

下面来讲解如何读取本地缓存的数据

读取缓存的时候我们只需要知道文件名就可以了,下面的readObject方法实现了根据文件名读取缓存数据。其实很多东西是和上面保存数据对应的。

readObject

 /**
* 读取对象
*
* @param file
* @return
* @throws IOException
*/
public static Serializable readObject(String file) {
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = AppContext.getInstance().openFileInput(file);
ois = new ObjectInputStream(fis);
return (Serializable) ois.readObject();
} catch (FileNotFoundException e) {
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
ois.close();
} catch (Exception e) {
}
try {
fis.close();
} catch (Exception e) {
}
}
return null;
}

运用

下面的代码演示了如何用上面的知识存储和读取网络数据

 String key = "codelist_" +  mCategory.getValue()  + "_" + + page ;
String result = "";
//cache
if (HttpUtil.isNetworkConnected()) {
result = HttpUtil.http_get(AppContext.getInstance(), url );
HttpUtil.saveObject(result, key);
result = (String) HttpUtil.readObject(key);
} else {
result = (String) HttpUtil.readObject(key);
if (result == null)
result = "erro";
}

当网络畅通时,从服务器获取数据( HttpUtil.http_get(AppContext.getInstance(), url )),同时将数据保存到本地(HttpUtil.saveObject),而当网络不可用时,直接从本地读取缓存的数据,不跟服务器发生交互。

其中HttpUtil是跟网络相关的工具类,这里涉及到它的三个方法:

 isNetworkConnected()判断网络是否可用
saveObject上面已经给出了实现
readObject上面已经给出了实现
http_get读取指定url的服务器数据

AppContext.getInstance()是我自己写的,是为了方便在HttpUtil的静态方法中获得Context对象。

这里的key就是文件名。

 

额外的需求

有时候我们还有这样的需求,当用户在指定间隔时间内读取同一数据源时,从本地获取,超过这个时间间隔从网络获取,这样做的目的是节省用户的流量,同时也避免了每次从网络获取数据造成的界面延迟。

下面实现了如何根据时间间隔判断是否需要刷新服务器数据,true表示不需要,false表示需要(很别扭是吧,这跟isCacheDataFailure这个命名有关系):

 public static boolean isCacheDataFailure(String cachefile) {
boolean failure = false;
File data = AppContext.getInstance().getFileStreamPath(cachefile);
if (data.exists()
&& (System.currentTimeMillis() - data.lastModified()) > CACHE_TIME)
failure = true;
else if (!data.exists())
failure = true;
return failure;
}

将当前时间和文件的修改时间做比较 ,CACHE_TIME是一个固定值(毫秒),你可以替换成任意int类型。

将这个判断条件加入,然后上面的代码改成:

 String key = "codelist_" +  mCategory.getValue()  + "_" + + page ;
String result = "";
//cache
if (HttpUtil.isNetworkConnected() && HttpUtil.isCacheDataFailure(key)) {
result = HttpUtil.http_get(AppContext.getInstance(), url );
HttpUtil.saveObject(result, key);
result = (String) HttpUtil.readObject(key);
} else {
result = (String) HttpUtil.readObject(key);
if (result == null)
result = "erro";
}

完善

上面的步骤对于一般应用来说已经够用了,但是在要求比较高的情况下,我们还得考虑随着时间的流逝,缓存数据会越来越多,因此我们需要增加删除过期缓存的功能,原理就是设置一个阀值,在保存缓存的时候,判断当前缓存的总量是否大于阀值,如果是则删除时间较早的缓存。

这个实现起来有点复杂,可以考虑更简单的方案,定期检查(或者用户每打开一次程序)缓存总量,当大于阀值,提示用户主动删除。具体实现就不多说了。

注:openFileOutput()方 法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data/<package name>/files目录,如: /data/data/cn.itcast.action/files/itcast.txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开/data/data/<package name>/files目录就可以看到该文件。
openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为: Context.MODE_PRIVATE    =  0
Context.MODE_APPEND    =  32768
Context.MODE_WORLD_READABLE =  1
Context.MODE_WORLD_WRITEABLE =  2
Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
如果希望文件被其他应用读和写,可以传入:
openFileOutput(“itcast.txt”, Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);

【转】android中如何实现离线缓存的更多相关文章

  1. android中如何实现离线缓存

    离线缓存就是在网络畅通的情况下将从服务器收到的数据保存到本地,当网络断开之后直接读取本地文件中的数据. 将网络数据保存到本地: 你可以自己写一个保存数据成本地文件的方法,保存在android系统的任意 ...

  2. android中图片的三级缓存cache策略(内存/文件/网络)

    实现图片缓存也不难,需要有相应的cache策略.这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且 ...

  3. 简单地Android中图片的三级缓存机制

    我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存 ...

  4. Android中图片的三级缓存策略

    在开发过程中,经常会碰到进行请求大量的网络图片的样例.假设处理的不好.非常easy造成oom.对于避免oom的方法,无非就是进行图片的压缩.及时的回收不用的图片.这些看似简单可是处理起来事实上涉及的知 ...

  5. Android之 -WebView实现离线缓存阅读

    前言 本篇博客要实现的是一个离线下载和离线阅读的功能,这是很多阅读类app都常见的一个功能,典型的应用就是网易新闻.什么是离线下载?其实这个概念是比较模糊,是离线之后下载呢,还是下载之后离线,但稍微有 ...

  6. Android中图片的三级缓存

    为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi ...

  7. Android 中的缓存机制与实现

    Android开发本质上就是手机和互联网中的web服务器之间进行通信,就必然需要从服务端获取数据,而反复通过网络获取数据是比较耗时的,特别是访问比较多的时候,会极大影响了性能,Android中可通过二 ...

  8. Android中的缓存机制与实现

    分步阅读 Android开发本质上就是手机和互联网中的web服务器之间进行通信,就必然需要从服务端获取数据,而反复通过网络获取数据是比较耗时的,特别是访问比较多的时候,会极大影响了性能,Android ...

  9. 缓存AsimpleCache -- 解决Android中Sharedpreferences无法存储List数据/ASimpleCache

    Sharedpreferences想必大家在项目中都经常会用到,但是如果需要在本地需要存储比较多的数据,存储一个集合的时,发现Sharedpreferences并不 是那么好使了. 分析 如果需要在本 ...

随机推荐

  1. 安装ruby-pg报错解决

    ruby用pgsql做orm的时候,需要安装ruby-pg库,默认编译安装会提示缺少xx头文件 max下,我用的傻瓜式pgsql.app gem install pg -- --with-pg-con ...

  2. java一位数组求平均值,小数

    package com.c2; //注意数据类型,float public class Col {// public static void main(String[] args) { float m ...

  3. 杂项-Java:JCP

    ylbtech-杂项-Java:JCP JCP(Java Community Process) 是一个开放的国际组织,主要由Java开发者以及被授权者组成,职能是发展和更新. 1. 中文名:jcp 外 ...

  4. Java-Maven-Runoob:Maven 自动化部署

    ylbtech-Java-Maven-Runoob:Maven 自动化部署 1.返回顶部 1. Maven 自动化部署 项目开发过程中,部署的过程包含需如下步骤: 将所的项目代码提交到 SVN 或者代 ...

  5. mybatis如何防止sql注入(2)

    Mybatis框架下SQL注入漏洞修复建议1. 模糊查询like SQL注入修复建议按照新闻标题对新闻进行模糊查询,可将SQL查询语句设计如下:select * from news where ti ...

  6. springboot成神之——websocket发送和请求消息

    本文介绍如何使用websocket发送和请求消息 项目目录 依赖 DemoApplication MessageModel WebConfig WebSocketConfig HttpHandshak ...

  7. oracle 查询中实现分页

    那么Oracle如何实现分页呢?--Oracle分页查询SELECT   * FROM   (     SELECT         ROWNUM R,YANGCQ_ID,YANGCQ_BRANCHI ...

  8. MySQL备份还原之三使用xtrabackup

    1 xtrabackup安装 1)解压源码包 tar -xzvf percona-xtrabackup-2.1.7.tar.gz 2)安装perl环境(DBI/DBD) yum install per ...

  9. QQ、邮箱、手机号 正则验证

    邮箱:/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/ 手机号:/^(((13[0-9]{1})|(15[0-9]{1 ...

  10. Java多线程-新特征-阻塞栈LinkedBlockingDeque

    对于阻塞栈,与阻塞队列相似.不同点在于栈是“后入先出”的结构,每次操作的是栈顶,而队列是“先进先出”的结构,每次操作的是队列头. 这里要特别说明一点的是,阻塞栈是Java6的新特征.. Java为阻塞 ...