原文地址: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. 关于bc 的scale .

    linux下的bc命令可以设置结果的位数,通过 scale. 比如: $ echo "scale=4; 1.2323293128 / 1.1" | bc -l1.1202 但是sc ...

  2. 【转】wireshark抓包工具详细说明及操作使用

      wireshark是非常流行的网络封包分析软件,功能十分强大.可以截取各种网络封包,显示网络封包的详细信息.使用wireshark的人必须了解网络协议,否则就看不懂wireshark了. 为了安全 ...

  3. netty中的UDP

    UDP 提供了向多个接收者发送消息的额外传输模式: 多播——传输到一个预定义的主机组: 广播——传输到网络(或者子网)上的所有主机. 本示例应用程序将通过发送能够被同一个网络中的所有主机所接收的消息来 ...

  4. 杂项:UN-标准通用置标语言

    ylbtech-杂项:标准通用置标语言 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部     6.返回顶部   7.返回顶部   8.返回顶部   9.返回顶部 ...

  5. PHP实现四种基本排序算法 得多消化消化

    1.冒泡排序 // 冒泡排序 思路分析:在要排序的一组数中,对当前还未排好的序列,从前往后对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒.即,每当两相邻的数比较后发现它们的排序与排序 ...

  6. Python Twisted架构英文版

    原作出处:twisted-intro 作者:Dave 转载声明:版权归原作出处所有,转载只为让更多人看到这部优秀作品合集,如果侵权,请留言告知 Twisted Introduction This mu ...

  7. Oracle 10g RAC 如何配置 VIP IPMP

    metalink note 283107.1介绍了如何设置VIP的IPMP,此处记录一下设置过程. o Existing 10g RAC installation ^^^^^^^^^^^^^^^^^^ ...

  8. js 判断浏览器类型大全

    /**** * 目前识别范围 * Microsoft Internet Explorer IE浏览器 * --> TheWorld 世界之窗 * --> TT浏览器 * --> 36 ...

  9. mfs教程(三)

    mfs文件系统(三) 使用  MooseFS 一.挂载文件系统 启动管理服务器(master server)和数据服务器(chunkservers) (chunkservers一个是必需的,但至少两个 ...

  10. mysql外键约束总结

    总结三种MySQL外键约束方式 如果表A的主关键字是表B中的字段,则该字段称为表B的外键,表A称为主表,表B称为从表.外键是用来实现参照完整性的,不同的外键约束方式将可以使两张表紧密的结合起来,特别是 ...