【转】android中如何实现离线缓存

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

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

将网络数据保存到本地:

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

saveObject

 1 public static boolean saveObject(Serializable ser, String file) {
 2     FileOutputStream fos = null;
 3     ObjectOutputStream oos = null;
 4     try {
 5         fos = AppContext.getInstance().openFileOutput(file, AppContext.getInstance().MODE_PRIVATE);
 6         oos = new ObjectOutputStream(fos);
 7         oos.writeObject(ser);
 8         oos.flush();
 9         return true;
10     } catch (Exception e) {
11         e.printStackTrace();
12         return false;
13     } finally {
14         try {
15             oos.close();
16         } catch (Exception e) {
17         }
18         try {
19             fos.close();
20         } catch (Exception e) {
21         }
22     }
23 }

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

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

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

1 {"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

 1 /**
 2  * 读取对象
 3  *
 4  * @param file
 5  * @return
 6  * @throws IOException
 7  */
 8 public static Serializable readObject(String file) {
 9     FileInputStream fis = null;
10     ObjectInputStream ois = null;
11     try {
12         fis = AppContext.getInstance().openFileInput(file);
13         ois = new ObjectInputStream(fis);
14         return (Serializable) ois.readObject();
15     } catch (FileNotFoundException e) {
16     } catch (Exception e) {
17         e.printStackTrace();
18     } finally {
19         try {
20             ois.close();
21         } catch (Exception e) {
22         }
23         try {
24             fis.close();
25         } catch (Exception e) {
26         }
27     }
28     return null;
29 }

运用

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

 1 String key = "codelist_" +  mCategory.getValue()  + "_" + + page ;
 2 String result = "";
 3 //cache
 4 if (HttpUtil.isNetworkConnected()) {
 5         result = HttpUtil.http_get(AppContext.getInstance(), url );
 6         HttpUtil.saveObject(result, key);
 7         result = (String) HttpUtil.readObject(key);
 8 } else {
 9     result = (String) HttpUtil.readObject(key);
10     if (result == null)
11         result = "erro";
12 }

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

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

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

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

这里的key就是文件名。

 

额外的需求

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

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

 1 public static boolean isCacheDataFailure(String cachefile) {
 2     boolean failure = false;
 3     File data = AppContext.getInstance().getFileStreamPath(cachefile);
 4     if (data.exists()
 5             && (System.currentTimeMillis() - data.lastModified()) > CACHE_TIME)
 6         failure = true;
 7     else if (!data.exists())
 8         failure = true;
 9     return failure;
10 }

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

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

 1 String key = "codelist_" +  mCategory.getValue()  + "_" + + page ;
 2 String result = "";
 3 //cache
 4 if (HttpUtil.isNetworkConnected() && HttpUtil.isCacheDataFailure(key)) {
 5         result = HttpUtil.http_get(AppContext.getInstance(), url );
 6         HttpUtil.saveObject(result, key);
 7         result = (String) HttpUtil.readObject(key);
 8 } else {
 9     result = (String) HttpUtil.readObject(key);
10     if (result == null)
11         result = "erro";
12 }

完善

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

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

注: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);

时间: 2024-10-09 21:46:09

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

android中如何实现离线缓存

离线缓存就是在网络畅通的情况下将从服务器收到的数据保存到本地,当网络断开之后直接读取本地文件中的数据. 将网络数据保存到本地: 你可以自己写一个保存数据成本地文件的方法,保存在android系统的任意目录(当然是有权限的才行),但是在这种情况下使用Context的openFileOutput方法最简便也最符合我们的场景,下面的saveObject方法演示了如何用openFileOutput将数据保存在本地的一个文件中: saveObject public static boolean saveO

Android记录25-WebView实现离线缓存阅读

Android记录25-WebView实现离线缓存阅读 前言 本篇博客要实现的是一个离线下载和离线阅读的功能,这是很多阅读类app都常见的一个功能,典型的应用就是网易新闻.什么是离线下载?其实这个概念是比较模糊,是离线之后下载呢,还是下载之后离线,但稍微有点脑子的人都知道没有网络之后怎么下载呢?所以离线下载这个功能是"在有网络的情况下,把资源下载到本地",离线阅读就是"在没有网络或者网络不好的时候,阅读本地好缓存的文章资源".这样就很清楚我们要的这两个具体的功能需求

Android中图片的三级缓存策略

一.简介 现在的Android应用程序中,不可避免的都会使用到图片,如果每次加载图片的时候都要从网络重新拉取,这样不但很耗费用户的流量,而且图片加载的也会很慢,用户体验很不好.所以一个应用的图片缓存策略是很重要的.通常情况下,Android应用程序中图片的缓存策略采用"内存-本地-网络"三级缓存策略,首先应用程序访问网络拉取图片,分别将加载的图片保存在本地SD卡中和内存中,当程序再一次需要加载图片的时候,先判断内存中是否有缓存,有则直接从内存中拉取,否则查看本地SD卡中是否有缓存,SD

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

我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存缓存中,缓存到强引用中 也就是LruCache中.如果强引用中空间不足,就会将较早存储的图片对象驱逐到软引用(softReference)中存储,然后将图片缓存到文件(内部存储外部存储)中:读取图片的时候,先读取内存缓存,判断强引用中是否存在图片,如果强引用中存在,则直接读取,如果强引用中不存在,则

查询android中各个应用的缓存

如何查询android手机中各个应用的缓存,很多手机卫士都有这个功能,但是从官方文档中找不到相应的api,那么问题就来了...如何实现这个功能?android开源的好处就是你可以翻系统源码,找到系统中设置的源码,系统设置中的应用详情里面就有缓存大小.从源码中可以看到Setting应用是调用的PackageManager中的getPackageSizeInfo方法,该方法接受两个参数,一个是包名,一个是名为IPackageStatsObserver的aidl对象,那么我们导入IPackageSta

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

实现图片缓存也不难,需要有相应的cache策略.这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中 1.简介 现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多. 现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wi

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

前言 本篇博客要实现的是一个离线下载和离线阅读的功能,这是很多阅读类app都常见的一个功能,典型的应用就是网易新闻.什么是离线下载?其实这个概念是比较模糊,是离线之后下载呢,还是下载之后离线,但稍微有点脑子的人都知道没有网络之后怎么下载呢?所以离线下载这个功能是”在有网络的情况下,把资源下载到本地“,离线阅读就是”在没有网络或者网络不好的时候,阅读本地好缓存的文章资源“.这样就很清楚我们要的这两个具体的功能需求了. 实现思路 小巫这里提供两个实现思路,一个就是自己写逻辑,一个是通过WebView

Android中的缓存机制与实现

分步阅读 Android开发本质上就是手机和互联网中的web服务器之间进行通信,就必然需要从服务端获取数据,而反复通过网络获取数据是比较耗时的,特别是访问比较多的时候,会极大影响了性能,Android中可通过二级缓存来减少频繁的网络操作,减少流量.提升性能. 方法/步骤 二级缓存工作机制 所谓二级缓存实际上并不复杂,当Android端需要获得数据时比如获取网络中的图片,我们首先从内存中查找(按键查找),内存中没有的再从磁盘文件或sqlite中去查找,若磁盘中也没有才通过网络获取:当获得来自网络的

Android 中的缓存机制与实现

Android开发本质上就是手机和互联网中的web服务器之间进行通信,就必然需要从服务端获取数据,而反复通过网络获取数据是比较耗时的,特别是访问比较多的时候,会极大影响了性能,Android中可通过二级缓存来减少频繁的网络操作,减少流量.提升性能. 一.二级缓存工作机制 所谓二级缓存实际上并不复杂,当Android端需要获得数据时比如获取网络中的图片,我们首先从内存中查找(按键查找),内存中没有的再从磁盘文件或sqlite中去查找,若磁盘中也没有才通过网络获取:当获得来自网络的数据,就以key-