相信很多同学都用过okhttp3这个强大的网络请求框架,应该会发现okhttp3中也封装了一个DiskLruCache磁盘缓存类,具体这个怎么用的呢,下面就来讲一讲。
Okhttp3中封装的DiskLruCache类用于磁盘文件缓存,类似JakeWharton的DiskLruCache,但两者还是有区别的,继续看下去。
1.首先要引入okhttp3资源包,这里直接使用的是retrofit2,引入retrofit2后,会自动把OKhttp3的资源包引入。
//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
2.创建一个DiskLruCache对象,并设置缓存文件位置,代码如下:
// 获取图片缓存路径
fileCacheDir = getDiskCacheDir(context, "thumb");
if (!fileCacheDir.exists()) {
fileCacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.create(FileSystem.SYSTEM,fileCacheDir,getAppVersion(context),1,10*1024*1024);
其中,fileCacheDir是获取存储目录的File对象,FileSystem.SYSTEM是okhttp3实现的一个FileSystem对象,getAppVersion获取当前app的版本,10*1024*1024表示缓存空间大小为10M,很好理解。
3.写入文件操作,通过DiskLruCache中的方法edit获取对应key的Editor对象,通过这个Editor对象获取到输出流对象Sink,该类实现了OutputStream,可当做OutputStream来看,具体写入详情看下面代码。
/**
* 下载bitmap线程
*/
private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... strings) {
String imageUrl = Images.BASE_URL+strings[0];
try{
DiskLruCache.Snapshot snapShot = null;
// 生成图片URL对应的key
final String key = hashKeyForDisk(imageUrl);
// 查找key对应的缓存
snapShot = mDiskLruCache.get(key);
if (snapShot == null) {
// 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
Sink sink = editor.newSink(0);
boolean result = downloadImage(strings[0],sink);
if(result){
editor.commit();
} else {
editor.abort();
}
}
// 缓存被写入后,再次查找key对应的缓存
snapShot = mDiskLruCache.get(key);
}
...
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if(mHandler != null){
Message msg = new Message();
msg.obj = bitmap;
mHandler.sendMessage(msg);
}
}
}
其中,hashKeyForDisk是根据网络资源的url通过使用MD5算法计算的一个值。
downloadImage代码如下:
private boolean downloadImage(String name, final Sink sink){
boolean result = false;
Call<ResponseBody> call = mHttpService.listRepos(name);
try {
Response<ResponseBody> response = call.execute();
result = writeToDisk(response.body(),call.request().url().url().toString(),sink);
}catch (IOException ex){
ex.printStackTrace();
result = false;
}
return result;
}
private boolean writeToDisk(ResponseBody body,String imageUrl,Sink sink) {
System.out.println("writeToDisk url ---> "+imageUrl);
InputStream inputStream = null;
try {
//long fileSize = body.contentLength();
//long fileSizeDownloaded = 0;
byte[] fileReader = new byte[4096];
Buffer buffer = new Buffer();
inputStream = body.byteStream();
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
buffer.write(fileReader,0,read);
//fileSizeDownloaded += read;
sink.write(buffer,read);
buffer.clear();
}
if(buffer != null){
buffer.clear();
buffer.close();
}
if (inputStream != null) {
inputStream.close();
}
if (sink != null) {
sink.flush();
sink.close();
}
return true;
} catch (IOException e) {
return false;
}
}
写入的关键就是把网络获取的inputStream流写入到Buffer中,然后在通过Sink对象的write方法不断的写入到文件中,写入完成时调用commit。这里还有一种写法就是一次inputSteam流中数据全部写入到buffer中后,在一次通过write写入文件,不过不推荐使用这种方式,如果文件大会占用很大内存,所以建议写入一次buffer就写入文件,并清空buffer,等待下一轮写入操作。
4.读取操作
读取操作则需要先获取到Snapshot对象,通过Snapshot对象的getSource方法获取到一个Source对象,Source类实现了InputSteam的功能,可看做InputStream。通过Source的方法read将数据读取出来存放到Buffer中,读取完成后,获取Buffer的一个inputStream流,如果是图片即可通过BitmapFactory.decodeStream获取到bitmap对象,这里是以图片为例,代码如下:
/**
* 下载bitmap线程
*/
private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... strings) {
String imageUrl = Images.BASE_URL+strings[0];
try{
DiskLruCache.Snapshot snapShot = null;
// 生成图片URL对应的key
final String key = hashKeyForDisk(imageUrl);
// 查找key对应的缓存
snapShot = mDiskLruCache.get(key);
...
//获取资源的输出流,Source类似InputStream
Source source = snapShot.getSource(0);
Buffer buffer = new Buffer();
//读取4*1024数据放入buffer中并返回读取到的数据的字节长度
long ret = source.read(buffer,4*1024);
//判断文件是否读完
while (ret != -1){
ret = source.read(buffer,4*1024);
}
//获取到buffer的inputStream对象
InputStream inputStream = buffer.inputStream();
//使用BitmapFactory解析inputStream并返回一个Bitmap对象
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
if(inputStream != null){
inputStream.close();
}
if(buffer != null){
buffer.clear();
buffer.close();
}
if(source != null){
source.close();
}
if(snapShot != null){
snapShot.close();
}
return bitmap;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
...
}
}