Android图片缓存管理

在一个app中,图片资源是处处存在的,加载图片的流程一般是:

1 先从缓存中读取
2 若缓存中不存在,从SD卡中读取
3 若SD卡中也不存在,则从服务器拉取

后两个步骤纯碎属于业务逻辑,暂且不表,这里来看一下手Q使用的图片缓存管理策略。
说到缓存管理,首先谈一下java中的强引用和弱引用

  • 强引用:最普遍的引用,若一个对象具有强引用,那么GC绝不会回收它。如A a = new A()
  • 弱引用: 弱引用又分为以下三类:
    • 软引用(SoftReference): 这类引用只有当内存空间不足GC才会回收它
    • 弱引用(WeakReference): 这类引用拥有更短的生命周期,GC扫描过程中一旦发现了此类引用,不管当前内存是否足够,立即回收
    • 虚引用(PhantomRefence): 这类引用并不会决定对象的生命周期,如果一个对象仅持有虚引用,则任何时刻都可能被回收

下面来看看这样一个图片缓存类,为了更大限度使用缓存,它使用了强引用缓存(强引用)和弱引用缓存(弱引用)双重缓存,强引用缓存不会轻易被回收,用来保存常用数据,不常用的转入弱引用缓存。**

ImageCache.java

public class ImageCache {
    private static final String TAG = "ImageCache";

    //CustomLruCache是一个继承了LruCache的继承类,它代表强引用缓存,它的缓存大小一般由业务方提供
    private CustomLruCache<String, Drawable> mMemoryCache;// Default memory cache size

    //这里设置的是弱引用缓存以及它所占据的空间大小
    private static final int DEFAULT_MEM_CACHE_SIZE = 5; // 5MB
    private final HashMap<String, WeakReference<Drawable>> mRefCache = new HashMap<String, WeakReference<Drawable>>();

    public ImageCache(int memSize) {
        memSize = Math.max(memSize, DEFAULT_MEM_CACHE_SIZE);
        QLog.d(TAG, "Memory cache size = " + memSize + "MB");
        mMemoryCache = new CustomLruCache<String, Drawable>(memSize * 1024 * 1024) {

            //这里重写了LruCache的sizeOf方法,来计算每个图片资源所占用内存的字节数
            @Override
            protected int sizeOf(String key, Drawable drawable) {
                if (drawable instanceof BitmapDrawable) {
                    Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
                    if (bitmap != null) {
                        //若是bitmap位图则直接计算它的大小
                        return bitmap.getRowBytes() * bitmap.getHeight();
                    }
                    return 0;
                } else if (drawable instanceof AnimationDrawable) {
                    //若是逐帧动画,则首先获取它所有的帧数,再计算总共的大小
                    AnimationDrawable anim = (AnimationDrawable) drawable;
                    int count = anim.getNumberOfFrames();
                    int memSize = 0;
                    for (int i = 0; i < count; i++) {
                        Drawable dr = anim.getFrame(i);
                        if (dr instanceof BitmapDrawable) {
                            Bitmap bitmap = ((BitmapDrawable) dr).getBitmap();
                            if (bitmap != null) {
                                memSize += bitmap.getRowBytes() * bitmap.getHeight();
                            }
                        }
                    }
                    return memSize;
                }
                return 0;
            }
        };
    }

    //从缓存中获取图片
    public Drawable getImageFromMemCache(String key) {
        Drawable memDrawable = null;
        if (mMemoryCache != null) {
            //首先从强引用缓存中获取图片,若找到的话,把元素移动到CustomLruCache的最后面,从而保证它在LRU算法中最后被删除?
            //疑问,其实LinkedHashMap本身就存在LRU的算法机制,因此,get的时候,会自动移入到队列尾部
            memDrawable = mMemoryCache.remove(key);
            if (memDrawable != null) {
                memDrawable = memDrawable.getConstantState().newDrawable();
                mMemoryCache.put(key, memDrawable);
                return memDrawable;
            }
        }

        //强引用缓存中没有找到,开始在弱引用缓存中查找
        WeakReference<Drawable> ref = mRefCache.get(key);
        if (ref != null) {
            //若找到的话,这里是否添加一步,将其从弱引用缓存移入强引用缓存中比较好
            memDrawable = ref.get();
            if (memDrawable == null) {
                mRefCache.remove(key);
            }
        }
        return memDrawable;
    }

    //添加图片到缓存,这里不理解为什么要向强引用缓存和弱引用缓存都要添加一份
    public void addImageToCache(String data, Drawable drawable) {
        // Add to memory cache
        if (mMemoryCache != null && mMemoryCache.get(data) == null) {
            mMemoryCache.put(data, drawable);
            mRefCache.put(data, new WeakReference<Drawable>(drawable));
        }
    }

    //从缓存中删除资源
    public void removeImageFromCache(String data) {
        if (mRefCache != null) {
            mRefCache.remove(data);
        }
        if (mMemoryCache != null) {
            mMemoryCache.remove(data);
        }
    }

    public Drawable getImageFromDiskCache(String pathName) {
        // TODO 暂不支持disk cache
        return null;
    }

    public void clearCaches() {
        // mDiskCache.clearCache();
        mMemoryCache.evictAll();
        mRefCache.clear();
    }
}   

整个缓存策略是使用弱引用缓存和强引用缓存配合使用,并结合LRUCache,在尽可能地利用缓存的基础上,也大大提高了缓存命中率。我个人觉得这个类有改进的地方,比如,当LRUCache在移除元素的时候,默认是直接删除掉。这里更好的方式是重写LRUCache的entryRemoved方法,使得强引用缓存满的时候,会根据LRU算法将最近最久没有被使用的图片自动移入弱引用缓存,如下:

  mMemoryCache = new CustomLruCache<String, Drawable>(memSize * 1024 * 1024) {

    //这里重写了LruCache的sizeOf方法,来计算每个图片资源所占用内存的字节数
      @Override
      protected int sizeOf(String key, Drawable drawable) {
                .........
      }

      当强引用缓存满时,会自动调用这个方法,这时候,将移除的元素添加到弱引用缓存中
      @Override
      protected void entryRemoved(boolean evicted, String key, Drawable oldDrawable, Drawable newDrawable) {
            if (oldDawable != null) {
                mRefCache.put(data, new WeakReference<Drawable>(oldDawable));
            }
      }
  };    

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值