okhttp+DiskLruCache 实现网络缓存

3,918 阅读3分钟
原文链接: www.68blog.com

写在前面

因项目需要做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动态权限申请等;

支持一下 扫一扫,支持一下