使用Okhttp3中的DiskLruCache进行文件缓存

相信很多同学都用过okhttp3这个强大的网络请求框架,应该会发现okhttp3中也封装了一个DiskLruCache磁盘缓存类,具体这个怎么用的呢,下面就来讲一讲。

Okhttp3中装的DiskLruCache类用于磁盘文件缓存类似JakeWhartonDiskLruCache,但两者还是有区别的,继续看下去。

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);
            ...
        }
    }
到这里主要的写入和读取操作已经写完了,Okhttp3的DiskLruCache同样也有 其他操作,比如删除某个key的缓存,flush操作等基本都没变化,这里就不再重复的说明了。

不难看出okhttp3中封装的DiskLruCache与 JakeWharton 的DiskLruCache功能类似都实现了磁盘缓存功能,所以如果有使用okhttp3的同学在需要用磁盘缓存的时候不妨使用OKhttp3自带的DiskLruCache,可以降低代码冗余。当然使用过程中还要看自己的习惯。

如有写的不准确的地方,请多多指教,互相学习。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值