写在前面
因项目需要做App离线缓存,本身okhttp是带有缓存功能的,但是太鸡肋了,所以还是借助DiskLruCache完成离线缓存;
运行坏境
1、okhttp:大神基于okhttp封装的库;
2、DiskLruCache:目前的版本是2.0.2
开始编码
这里我是直接把上面封装okhttp的工具库代码下载下来,再进行修改源码;
别忘了使用DiskLruCache要在build.gradle中引用,例:
compile 'com.jakewharton:disklrucache:2.0.2'
新建缓存工具类
/**
* ┏┓ ┏┓
* ┏┛┻━━━┛┻┓
* ┃ ┃
* ┃ ━ ┃
* ┃ ┳┛ ┗┳ ┃
* ┃ ┃
* ┃ ┻ ┃
* ┃ ┃
* ┗━┓ ┏━┛
* ┃ ┃神兽保佑
* ┃ ┃代码无BUG!
* ┃ ┗━━━┓
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
* ━━━━━━神兽出没━━━━━━
* Created by wdh on 2017/4/3 10:36.
*/
public class BasicCache {
private DiskLruCache diskCache;
private String fileName = "cache";
public BasicCache(String filePath, long maxDiskSize) {
try {
diskCache = DiskLruCache.open(new File(filePath, fileName), 1, 1, maxDiskSize);
} catch (IOException exc) {
diskCache = null;
}
}
/**
* url转MD5
*
* @param url
* @return
*/
private String urlToMD5(HttpUrl url) {
return MD5.getMD5(url.toString());
}
/**
* 添加缓存数据
*
* @param response
*/
public void addCache(String date, Response response) {
if (diskCache == null) {
return;
}
Buffer buffer = new Buffer();
try {
buffer.write(date.getBytes());
byte[] rawResponse = buffer.readByteArray();
DiskLruCache.Editor editor = diskCache.edit(urlToMD5(response.request().url()));
editor.set(0, new String(rawResponse, Charset.defaultCharset()));
editor.commit();
buffer.clone();
} catch (IOException exc) {
buffer.clone();
}
}
/**
* 获取缓存数据
*
* @param request
* @return
*/
public ResponseBody getCache(Request request) {
if (diskCache == null) {
return null;
}
String cacheKey = urlToMD5(request.url());
try {
DiskLruCache.Snapshot cacheSnapshot = diskCache.get(cacheKey);
if (cacheSnapshot != null) {
return ResponseBody.create(null, cacheSnapshot.getString(0).getBytes());
} else {
return null;
}
} catch (IOException exc) {
return null;
}
}
/**
* 删除缓存
*
* @param request
*/
public void deleteCache(Request request) {
if (diskCache == null) {
return;
}
try {
String cacheKey = urlToMD5(request.url());
DiskLruCache.Snapshot cacheSnapshot = diskCache.get(cacheKey);
if (cacheSnapshot != null) {
diskCache.remove(cacheKey);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class CacheConfig {
/**
* 缓存空间大小,默认5MB
*/
private long disk_size = 5 * (1024 * 1024);
/**
* 缓存目录
*/
private String cache_path;
public long getDisk_size() {
return disk_size;
}
public CacheConfig setDisk_size(long disk_size) {
this.disk_size = disk_size;
return this;
}
public String getCache_path() {
return cache_path;
}
public CacheConfig setCache_path(String cache_path) {
this.cache_path = cache_path;
return this;
}
}
通过调用DiskLruCache.open来返回一个DiskLruCache实例,分别需要四个参数(File directory, int appVersion, int valueCount, long maxSize),directory为缓存文件在磁盘中的路径,appVersion为app版本号,如果没有强迫症什么的填1就可以了,valueCount是单个节点对应的数据个数,通俗点说就是一个key对应多少个value,正常填1即可,maxSize是缓存的空间大小,代码简单就不一一讲解了;
然后切换到请求网络的代码,在前面加上取出缓存的代码,例:
if (executorService == null) {
executorService = Executors.newCachedThreadPool();
}
Runnable syncRunnable = new Runnable() {
@Override
public void run() {
//取出缓存数据
ResponseBody responseBody = mBasicCache.getCache(requestCall.getRequest());
if (responseBody == null) {
return;
}
try {
sendSuccessResultCallback(new ResponseResult(true, responseBody.string()), finalCallback, id);
} catch (IOException e) {
e.printStackTrace();
}
}
};
executorService.execute(syncRunnable);
这里不建议在主线程执行但不断的new Thread()又可能造成线程没有被及时回收造成OOM等不必要的情况,所以使用ExecutorService去管理,适用于生存期短暂的异步任务,在使用缓存型池时,它会先查看池中有没有以前创建的线程,如果有,就复用,如果没有,就新建;
接下来在网络请求成功的回调里面加上添加缓存的代码,如:
mBasicCache.addCache((String) o, response);
OK,最后在Application中配置,例:
//okhttp
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(20000, TimeUnit.MILLISECONDS)
.readTimeout(20000, TimeUnit.MILLISECONDS)
.build();
//配置缓存
CacheConfig cacheConfig = new CacheConfig();
cacheConfig.setCache_path(APP_SDCARD_DIR).setDisk_size(5 * (1024 * 1024));
OkHttpUtils.initClient(okHttpClient, cacheConfig);
测试结果

总结
如果你用的是其它的网络请求框架,原理也一样,在请求前看本地是否有缓存数据,在请求完成后每次都更新本地的缓存文件,这样也能保证本地的缓存数据是最后一次请求最新的,本文的缓存文件名用的请求URL地址转MD5作为文件的名称,本文没有对缓存数据进行加密的,如若需要可自行按照自己的加密算法进行加密;
android自带的LruCache是基于内存的缓存,退出应用也就没有了,DiskLruCache则是基于磁盘缓存,对于DiskLruCache就不再做多阐述,网上相关的资料很多,文章如若有误,欢迎指出;
最后附上DEMO地址,里面的okhttplib类库可以直接拿出来用,工具类包含6.0动态权限申请等;
支持一下 扫一扫,支持一下