13.2-DiskLruCache 磁盘缓存

概述:

Google又提供了一套硬盘缓存的解决方案:DiskLruCache(非Google官方编写,但获得官方认证)。
那么我们先来看一下有哪些应用程序已经使用了DiskLruCache技术。在我所接触的应用范围里,Dropbox、Twitter、网易新闻等都是使用DiskLruCache来进行硬盘缓存的,其中Dropbox和Twitter大多数人应该都没用过,那么我们就从大家最熟悉的网易新闻开始着手分析,来对DiskLruCache有一个最初的认识吧。
使用:
DiskLruCache没有限制数据的缓存位置,可以自由地进行设定,但是通常情况下多数应用程序都会将缓存的位置选择为 /sdcard/Android/data//cache 这个路径。选择在这个位置有两点好处:第一,这是存储在SD卡上的,因此即使缓存再多的数据也不会对手机的内置存储空间有任何影响,只要SD卡空间足够就行。第二,这个路径被Android系统认定为应用程序的缓存路径,当程序被卸载的时候,这里的数据也会一起被清除掉,这样就不会出现删除程序之后手机上还有很多残留数据的问题。

1.依赖

implementation 'com.jakewharton:disklrucache:2.0.2'

2.初始化

参数:

  • directory 第一个是缓存文件文件的位置
  • 是应用程序的版本号
  • 表示同一个key可以对应多少个缓存文件,一般情况下我们都是传1
  • 参数表示最大可以缓存多少字节的数据
 

  public DiskCache(File directory, long maxSize) {
        try {
            if (mDiskLruCache == null || mDiskLruCache.isClosed()) {

                mDiskLruCache = DiskLruCache.open(directory, 1, 1, maxSize);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.保存数据

 /**
     * 保存bitmap到磁盘
     * 通过图片url 然后md5 加密的内容,作为key  来保存bitmap 对象
     *
     * @param url    图片请求url  --》 md5  去除特殊字符
     * @param bitmap
     */
    public void saveBitmap(final String url) {
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    String key = Utils.hashKeyForDisk(url);
                    //editor 操作数据保存逻辑
                    DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                    OutputStream outputStream = editor.newOutputStream(0);
                    // 下载图片
                    if (downloadImage(url, outputStream)) {
                        editor.commit();
                    } else {
                        editor.abort();
                    }
                    //别忘了关闭流和提交编辑
                    outputStream.close();
                    mDiskLruCache.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

4.获取数据

 
 /**
     * 通过url  获取md5加密后的key。然后通过key 获取bitmap 对象
     *
     * @param url
     * @return
     */
    public Bitmap getBitmap(String url) {
        //使用DiskLruCache获取缓存,需要传入key,而key是imageUrl加密后的字符串,
        Bitmap bitmap = null;
        String key = Utils.hashKeyForDisk(url);
        //通过key获取的只是一个快照
        DiskLruCache.Snapshot snapshot = null;
        try {
            snapshot = mDiskLruCache.get(key);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (snapshot != null) {
            InputStream inputStream = snapshot.getInputStream(0);//类似写缓存时候,传入的是缓存的编号
            //可以使用bitmapFactory
            bitmap = BitmapFactory.decodeStream(inputStream);
        }

        return bitmap;
    }

缓存文件

每个图片对应一个乱码形式名称的文件
在这里插入图片描述

  • journal文件解读 对图片操作的日志文件
libcore.io.DiskLruCache
1
100
1

DIRTY 335c4c6028171cfddfbaae1a9c313c52
CLEAN 335c4c6028171cfddfbaae1a9c313c52 2342
REMOVE 335c4c6028171cfddfbaae1a9c313c52
DIRTY 1ab96a171faeeee38496d8b330771a7a
CLEAN 1ab96a171faeeee38496d8b330771a7a 1600
READ 335c4c6028171cfddfbaae1a9c313c52
READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
  • journal文件头
    第一行:固定字符串libcore.io.DiskLruCache
    第二行:DiskLruCache的版本号,这个值恒为1。
    第三行:应用程序的版本号。每当版本号改变,缓存路径下存储的所有数据都会被清空,因为DiskLruCache认为应用更新,所有的数据都应重新获取。
    第四行:指每个key对应几个文件,一般为1。
    第五行:空行
  • journal文件内容
    DIRTY:第六行以DIRTY前缀开始,后面跟着缓存文件的key,表示一个entry正在被写入。
    CLEAN:当写入成功,就会写入一条CLEAN记录,后面的数字记录文件的长度,如果一个key可以对应多个文件,那么就会有多个数字
    REMOVE:表示写入失败,或者调用remove(key)方法的时候都会写入一条REMOVE记录

涉及的类

Utils .java


public class Utils {

    private static final String TAG = "Utils";
    /**
     * 获取缓存文件夹,这里优先选择SD卡下面的android/data/packageName/cache/路径,若没有SD卡,就选择data/data/packageName/cache
     *
     * @param context    上下文环境
     * @param uniqueName 缓存文件夹名称
     * @return 返回缓存文件
     */
    public static File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        File file = new File(cachePath + File.separator + uniqueName);
        Log.d(TAG, "getDiskCacheDir: file="+file.getAbsolutePath());
        return file;
    }
    /**
     * 获取本App的版本号
     *
     * @param context context上下文
     * @return 返回版本号
     */
    public static int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }
    /**
     * 给字符串来个md5加密,
     * @param key 需要加密的string
     * @return 返回加密后的string ,或者加密失败,就返回string的哈希值
     */
    public static String hashKeyForDisk(String key) {
        String cacheKey;
        try {
            //md5加密
            MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            //若md5加密失败,就用哈希值
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }
    /**
     * 字节数组转为十六进制字符串
     * @param bytes 字节数组
     * @return 返回十六进制字符串
     */
    private static String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xFF & b);
            if (hex.length()==1){
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }
}

DiskCache.java

public class DiskCache {

    private static final String TAG = "DiskCache";
    DiskLruCache mDiskLruCache;
    // 初始化


    public DiskCache(File directory, long maxSize) {
        try {
            if (mDiskLruCache == null || mDiskLruCache.isClosed()) {

                mDiskLruCache = DiskLruCache.open(directory, 1, 1, maxSize);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 保存bitmap到磁盘
     * 通过图片url 然后md5 加密的内容,作为key  来保存bitmap 对象
     *
     * @param url    图片请求url  --》 md5  去除特殊字符
     * @param bitmap
     */
    public void saveBitmap(final String url, Bitmap bitmap) {
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    String key = Utils.hashKeyForDisk(url);
                    //editor 操作数据保存逻辑
                    DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                    OutputStream outputStream = editor.newOutputStream(0);
                    // 下载图片
                    if (downloadImage(url, outputStream)) {
                        editor.commit();
                    } else {
                        editor.abort();
                    }
                    //别忘了关闭流和提交编辑
                    outputStream.close();
                    mDiskLruCache.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    /**
     * 通过url  获取md5加密后的key。然后通过key 获取bitmap 对象
     *
     * @param url
     * @return
     */
    public Bitmap getBitmap(String url) {
        //使用DiskLruCache获取缓存,需要传入key,而key是imageUrl加密后的字符串,
        Bitmap bitmap = null;
        String key = Utils.hashKeyForDisk(url);
        //通过key获取的只是一个快照
        DiskLruCache.Snapshot snapshot = null;
        try {
            snapshot = mDiskLruCache.get(key);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (snapshot != null) {
            InputStream inputStream = snapshot.getInputStream(0);//类似写缓存时候,传入的是缓存的编号
            //可以使用bitmapFactory
            bitmap = BitmapFactory.decodeStream(inputStream);
        }

        return bitmap;
    }


    /**
     * 下载图片
     *
     * @param imgUrl       图片网址链接
     * @param outputStream 输出流对象
     * @return 返回时候完成下载成功
     */
    private boolean downloadImage(String imgUrl, OutputStream outputStream) {
        HttpURLConnection urlConnection = null;
        BufferedOutputStream out = null;
        BufferedInputStream in = null;
        try {
            URL url = new URL(imgUrl);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(), 2 * 1024);//Buffer输入流,8M大小的缓存
            out = new BufferedOutputStream(outputStream, 2 * 1024);
            int b;//正在读取的byte
            while ((b = in.read()) != -1) {
                out.write(b);
            }
            return true;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //关闭资源
        finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }
}

发布了118 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/chentaishan/article/details/104885798