Android图片之缓存策略

注意:本篇文章是本人阅读相关文章所写下的总结,方便以后查阅,所有内容非原创,侵权删。

本篇文章内容来自于:
1.Android开发艺术探索 任玉刚
2.Android图片缓存之Lru算法

目录

  1. 前言
    --1.1 缓存策略一般缓存顺序为:内存-存储设备-网络
    --1.2 缓存策略主要包含缓存的添加、获取和删除。(删除这步决定了要缓存算法)
    --1.3 常用缓存算法LRU(LruCache和DiskLruCache)
  2. LruCache缓存算法 内存缓存
    --2.1 LruCache的使用
  3. DiskLruCache 磁盘缓存
    --3.1 DiskLruCache的使用

1. 前言

1.1 缓存策略一般缓存顺序为:内存-存储设备-网络

缓存策略是一个通用的思想,可以用在很多场合。
通过缓存策略,我们不需要每次都从网络上请求图片,或者从存储设备中加载图片,这样就极大的提高了图片的加载效率以及产品的用户体验(减少流量消耗)。

具体做法为:
第一次从网络加载图片,将其缓存到存储设备上(这样下次使用这张图片不用再从网络上获取,节省流量),还会在内存中缓存一份(为提高应用的用户体验,因为内存中加载图片比从存储设备中加载图片要快)。
这样应用打算从网络上请求一张图片时:程序先从内存中去获取,如果内存中没有再到存储设备中去获取,最后再到网络上下载。

1.2 缓存策略主要包含缓存的添加、获取和删除。

缓存策略,没有统一的标准。
缓存策略主要包含缓存的添加、获取和删除。

为什么要删除呢?因为不管内存缓存还是存储设备缓存,缓存大小都是有限制的,因为内存和诸如SD卡之类的存储设备都是有容量限制的。因此使用缓存的时候总要为缓存指定一个最大的容量,当缓存容量满了,但程序还需要向其添加缓存,这个时候就需要删除一些旧的缓存并添加新的缓存。如何定义缓存的新旧就是一种策略,不同的策略对应不同的缓存算法

1.3 常用缓存算法LRU(LruCache和DiskLruCache)

目前最常用的缓存算法是LRU,Lru是Least Recently Used的缩写,最近最少使用算法。这种算法的核心思想是:当缓存快满的时候,会淘汰近期最少使用的缓存目标

采用LRU算法的缓存是LruCache和DiskLruCache
LruCache用于实现内存缓存,DiskLruCache用作存储设备缓存。

2. LruCache缓存算法

基于LruCache实现内存缓存
LruCache是Android3.1所提供的缓存类,用support-v4兼容包可以兼容到早期的Android版本。

LruCache是一个泛型类,内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象
其提供了get和put方法来完成缓存的获取和添加操作,当缓存满的时候,LruCache会移除较早使用的缓存对象。然后再添加新的缓存对象。
LruCache是线程安全的。

强引用:直接的对象引用
软引用:当一个对象只有软引用存在时,系统内存不足的时候此对象会被gc回收。
弱引用:当一个对象只有弱引用存在时,此对象会随时被gc回收

2.1 LruCache的使用

1.LruCache的初始化
这里内存缓存的是Drawable 而不是Bitmap 理由是Drawable相对Bitmap来说有很大的内存优势

        int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小
        int mCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一
        mMemoryCache = new LruCache(mCacheSize) {
            //必须重写此方法,来测量Bitmap的大小
            @Override
            protected int sizeOf(String key, Drawable value) {
                if (value instanceof BitmapDrawable) {
                    Bitmap bitmap = ((BitmapDrawable) value).getBitmap();
                    return bitmap == null ? 0 : bitmap.getByteCount();
                }
                return super.sizeOf(key, value);
            }

            //某些特殊情况下,重写entryRemoved方法,LruCache移除缓存时会调用该方法,因此需要在这里完成一些资源回收工作。
        };

2.添加一个Drawable到内存缓存

/**
     * 添加Drawable到内存缓存
     *
     * @param key
     * @param drawable
     */
    private void addDrawableToMemoryCache(String key, Drawable drawable) {
        if (getDrawableFromMemCache(key) == null && drawable != null) {
            mMemoryCache.put(key, drawable);
        }
    }

3.从内存缓存中获取一个Drawable

/**
     * 从内存缓存中获取一个Drawable
     *
     * @param key
     * @return
     */
    public Drawable getDrawableFromMemCache(String key) {
        return mMemoryCache.get(key);
    }

4.从内存缓存中移除一个Drawable

/**
     * 从内存缓存中移除
     *
     * @param key
     */
    public void removeCacheFromMemory(String key) {
        mMemoryCache.remove(key);
    }

5.清空内存缓存

/**
     * 清理内存缓存
     */
    public void cleanMemoryCCache() {
        mMemoryCache.evictAll();
    }

3. DiskLruCache

DiskLruCache用作存储设备缓存。它通过将缓存对象写入文件系统从而实现缓存的效果。

DiskLruCache类并不是谷歌官方实现,需要自行下载,下载地址:https://github.com/JakeWharton/DiskLruCache

3.1 DiskLruCache的使用

1. DiskLruCache的初始化

        File cacheDir = context.getCacheDir();//指定的是数据的缓存地址,也可以选择SD卡上的缓存目录getExternalCacheDir()
        long diskCacheSize = 1024 * 1024 * 30;//最多可以缓存多少字节的数据,超过则会清除缓存。这里设置成了30M
        int appVersion = DiskLruUtils.getAppVersion(context);//指定当前应用程序的版本号,一般设置为1即可,当版本号发生改变时DiskLruCache会清空之前所有的缓存文件。但很多情况下即使改变,缓存文件也在。
        int valueCount = 1;//指定同一个key可以对应多少个缓存文件
        try {
            mDiskCache = DiskLruCache.open(cacheDir, appVersion, valueCount, diskCacheSize);
        } catch (Exception ex) {
        }

1. 写入一个文件到磁盘缓存

/**
     * 添加Bitmap到磁盘缓存
     *
     * @param key
     * @param value
     */
    private void addBitmapToDiskCache(String key, byte[] value) {
        OutputStream out = null;
        try {
            //DiskLruCache的缓存操作通过Editor完成。
            //Editor表示一个缓存对象的编辑对象,根据key就可以通过edit()获取Editor对象。
            //如果这个缓存正在被编辑,那么edit()就会返回null,即DiskLruCache不允许同时编辑一个缓存对象。
            DiskLruCache.Editor editor = mDiskCache.edit(key);
            if (editor != null) {
                //前面设置了一个节点只能有一个数据,则传入参数为0即可
                out = editor.newOutputStream(0);
                if (value != null && value.length > 0) {
                    out.write(value);
                    out.flush();
                    //必须通过commit来提交写入操作,否则不会真正的写入系统。
                    editor.commit();
                } else {
                    //如果出现异常,则可以通过abort来回退整个操作。
                    editor.abort();
                }
            }
            mDiskCache.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            DiskLruUtils.closeQuietly(out);
        }
    }

关于key的获取
一般根据图片url来获取对应的key
一般采用url的md5作为key,因为图片url可能有特殊字符。

    private String hashKeyFromUrl(String url) {
        String cacheKey;
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(url.getBytes());
            cacheKey = bytesToHexString(digest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(url.hashCode());
        }
        return cacheKey;
    }

    private String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

2. 从磁盘缓存中读取Drawable

/**
     * 从磁盘缓存中获取一个Drawable
     *
     * @param key
     * @return
     */
    public Drawable getDrawableFromDiskCache(String key) {
        try {
            DiskLruCache.Snapshot snapShot = mDiskCache.get(key);
            if (snapShot != null) {
                InputStream is = snapShot.getInputStream(0);
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
                //从磁盘中读取到之后 加入内存缓存
                addDrawableToMemoryCache(key, drawable);
                return drawable;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

3. 从磁盘缓存中移除

/**
     * 从磁盘缓存中移除
     *
     * @param key
     */
    public void removeCacheFromDisk(String key) {
        try {
            mDiskCache.remove(key);
        } catch (Exception e) {
        }
    }

4. 清空磁盘缓存

/**
     * 清理磁盘缓存
     */
    public void cleanDiskCache() {
        try {
            mDiskCache.delete();
        } catch (Exception e) {
        }
    }

你可能感兴趣的:(Android图片之缓存策略)