Android开源框架Universal-Image-Loader学习三——UsingFreqLimitedMemoryCache源码阅读



Universal-Image-Loader的内存缓存策略

1. 只使用的是强引用缓存

  • LruMemoryCache(这个类就是这个开源框架默认的内存缓存类,缓存的是bitmap的强引用)

2.使用强引用和弱引用相结合的缓存有

  • UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的bitmap)
  • LRULimitedMemoryCache(这个也是使用的lru算法,和LruMemoryCache不同的是,他缓存的是bitmap的弱引用)
  • FIFOLimitedMemoryCache(先进先出的缓存策略,当超过设定值,先删除最先加入缓存的bitmap)
  • LargestLimitedMemoryCache(当超过缓存限定值,先删除最大的bitmap对象)
  • LimitedAgeMemoryCache(当 bitmap加入缓存中的时间超过我们设定的值,将其删除)

3.只使用弱引用缓存

  • WeakMemoryCache(这个类缓存bitmap的总大小没有限制,唯一不足的地方就是不稳定,缓存的图片容易被回收掉)

继承关系:

public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache
public abstract class LimitedMemoryCache extends BaseMemoryCache
public abstract class BaseMemoryCache implements MemoryCache
public interface MemoryCache extends MemoryCacheAware<String, Bitmap>
@Deprecated
public interface MemoryCacheAware<K, V>

1、先来看MemoryCacheAware:

/* Interface for memory cache*/
@Deprecated
public interface MemoryCacheAware<K, V> {
    /*Puts value into cache by key
     * @return true - if value was put into cache successfully;false - if value was not put into cache
     */
    boolean put(K key, V value);

    /** Returns value by key. If there is no value for key then null will be returned. */
    V get(K key);

    /** Removes item by key */
    V remove(K key);

    /** Returns all keys of cache */
    Collection<K> keys();

    /** Remove all items from cache */
    void clear();
}

2、现在一般使用MemoryCache替代MemoryCacheAware:

MemoryCacheAware源码:

public interface MemoryCache extends MemoryCacheAware<String, Bitmap> {
}

3、BaseMemoryCache 源码:

/*为memory cache提供一些基本功能;提供object的引用(非强引用)存储*/
public abstract class BaseMemoryCache implements MemoryCache {
    /** 存储objects的非强引用,Collections.synchronizedMap保证线程安全*/
    private final Map<String, Reference<Bitmap>> softMap = Collections.synchronizedMap(new HashMap<String, Reference<Bitmap>>());

    @Override
    public Bitmap get(String key) {
        Bitmap result = null;
        Reference<Bitmap> reference = softMap.get(key);
        if (reference != null) {
            result = reference.get();
        }
        return result;
    }

    @Override
    public boolean put(String key, Bitmap value) {
        softMap.put(key, createReference(value));
        return true;
    }

    @Override
    public Bitmap remove(String key) {
        Reference<Bitmap> bmpRef = softMap.remove(key);
        return bmpRef == null ? null : bmpRef.get();
    }

    @Override
    public Collection<String> keys() {
        synchronized (softMap) {
            return new HashSet<String>(softMap.keySet());
        }
    }

    @Override
    public void clear() {
        softMap.clear();
    }

    /** Creates {@linkplain Reference not strong} reference of value */
    protected abstract Reference<Bitmap> createReference(Bitmap value);
}
 

4、LimitedMemoryCache源码:

/**
 * 限定的Cache.提供Object的存储。所有存储的bitmap的总内存大小不超过限定值
 * 注:该cache使用强引用和弱引用来存储Bitmaps;
 * 强引用——对于限额内的bitmaps
 * 弱应用——对于其他的Bitmaps
 */
public abstract class LimitedMemoryCache extends BaseMemoryCache {

    private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16;
    private static final int MAX_NORMAL_CACHE_SIZE = MAX_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024;

    private final int sizeLimit;
    /*AtomicInteger,一个提供原子操作的Integer的类,使得操作线程安全*/
    private final AtomicInteger cacheSize;

    /**
     * 包含存储objects的强引用。每个object都添加到最尾端;如果hard cache的大超过了限定值,首端的object将会被删除
     *(但它依然存在在softMap中,而且可以随时被GC回收)
     // 返回一个synchronizes封装的线程安全的List
        public static <T> List<T> synchronizedList(List<T> list)*/
    private final List<Bitmap> hardCache = Collections.synchronizedList(new LinkedList<Bitmap>());

    /**构造函数:sizeLimit单位bytes */
    public LimitedMemoryCache(int sizeLimit) {
        this.sizeLimit = sizeLimit;
        cacheSize = new AtomicInteger();
        /**检测避免sizeLimit值设置过大 */
        if (sizeLimit > MAX_NORMAL_CACHE_SIZE) {
            L.w("You set too large memory cache size (more than %1$d Mb)", MAX_NORMAL_CACHE_SIZE_IN_MB);
        }
    }

    /**操作成功返回true,操作失败返回false
       先尝试将Bitmap添加至hard cache,再将其添加至soft cache*/
    @Override
    public boolean put(String key, Bitmap value) {
        boolean putSuccessfully = false;

        int valueSize = getSize(value);    //抽象函数,返回Bitmap的大小值
        int sizeLimit = getSizeLimit();    //返回sizeLimit
        int curCacheSize = cacheSize.get();//返回cacheSize的当前值
         /**如果添加的bitmap的size大于sizeLimit,则直接不将其添加至hard cache*/
        if (valueSize < sizeLimit) {
            /**判断新添加的Object的valueSize加上当前cache中已有object的curCacheSize超过限定值,则会删除适当Bitmap*/
            while (curCacheSize + valueSize > sizeLimit) {
                Bitmap removedValue = removeNext();    //abstract函数,返回需要删除的下一个Bitmap
                if (hardCache.remove(removedValue)) {
                    curCacheSize = cacheSize.addAndGet(-getSize(removedValue));
                }
            }
            hardCache.add(value);          //添加到LinkedList<Bitmap>尾部
            cacheSize.addAndGet(valueSize);//即cacheSize+valueSize

            putSuccessfully = true;
        }
        // Add value to soft cache
        super.put(key, value);
        return putSuccessfully;
    }

    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            if (hardCache.remove(value)) {
                cacheSize.addAndGet(-getSize(value));
            }
        }
        return super.remove(key);
    }

    @Override
    publicvoid clear() {
        hardCache.clear();
        cacheSize.set(0);
        super.clear();
    }

    protected int getSizeLimit() {
        return sizeLimit;
    }

    protected abstract int getSize(Bitmap value);

    protected abstract Bitmap removeNext();
}
 

5、UsingFreqLimitedMemoryCache源码:(关键在于get中更新使用频率)

/**
 * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
 * exceed size limit. When cache reaches limit size then the bitmap which used the least frequently is deleted from
 * cache(删除最少使用频率的bitmap)
 * NOTE:使用强引用和弱引用;
 * 强引用:for limited count of Bitmaps (depends on cache size)
 * 弱应用:for all other cached Bitmaps
 */
public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache {
    /**
     * Contains strong references to stored objects (keys) and last object usage date (in milliseconds). If hard cache
     * size will exceed limit then object with the least frequently usage is deleted (but it continue exist at
     * {@link #softMap} and can be collected by GC at any time)
     */
    private final Map<Bitmap, Integer> usingCounts = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());

    public UsingFreqLimitedMemoryCache(int sizeLimit) {
        super(sizeLimit);
    }

    @Override
    public boolean put(String key, Bitmap value) {
        if (super.put(key, value)) {
            usingCounts.put(value, 0);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Bitmap get(String key) {
        Bitmap value = super.get(key);
        // 如果value存在,则usageCount+ 1;
        if (value != null) {
            Integer usageCount = usingCounts.get(value);
            if (usageCount != null) {
                // 更新value对应的usageCount的值
                usingCounts.put(value, usageCount + 1);
            }
        }
        return value;
    }

    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            usingCounts.remove(value);
        }
        return super.remove(key);
    }

    @Override
    public void clear() {
        usingCounts.clear();
        super.clear();
    }

    @Override
    protected int getSize(Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override
    protected Bitmap removeNext() {
        Integer minUsageCount = null;
        Bitmap leastUsedValue = null;
        Set<Entry<Bitmap, Integer>> entries = usingCounts.entrySet();
        synchronized (usingCounts) {
            for (Entry<Bitmap, Integer> entry : entries) {
               //初始化leastUsedValue
                if (leastUsedValue == null) {
                    leastUsedValue = entry.getKey();
                    minUsageCount = entry.getValue();
                } else {
                //简单的比较得到最小值
                    Integer lastValueUsage = entry.getValue();
                    if (lastValueUsage < minUsageCount) {
                        minUsageCount = lastValueUsage;
                        leastUsedValue = entry.getKey();
                    }
                }
            }
        }
        usingCounts.remove(leastUsedValue);
        return leastUsedValue;
    }

    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}
时间: 2024-12-17 03:54:27

Android开源框架Universal-Image-Loader学习三——UsingFreqLimitedMemoryCache源码阅读的相关文章

Android 开源框架Universal-Image-Loader完全解析(三)---源代码解读

本篇文章主要是带大家从源码的角度上面去解读这个强大的图片加载框架,自己很久没有写文章了,感觉生疏了许多,距离上一篇文章三个月多了,确实是自己平常忙,换了工作很多东西都要去看去理解,然后加上自己也懒了,没有以前那么有激情了,我感觉这节奏不对,我要继续保持以前的激情,正所谓好记性不如烂笔头,有时候自己也会去翻看下之前写的东西,我觉得知识写下来比在脑海中留存的更久,今天就给大家来读一读这个框架的源码,我感觉这个图片加载框架确实写的很不错,读完代码自己也学到了很多.我希望大家可以先去看下Android

【 js 基础 】【 源码学习 】backbone 源码阅读(三)

最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-study)进行参考交流,有详细的源码注释,以及知识总结,同时 google 一下 backbone 源码,也有很多优秀的文章可以用来学习. 我这里主要记录一些偏设计方向的知识点.这篇文章主要讲 backbone.sync 中用到的 Rest 和 CRUD. 首先我们简单了解一下 REST: REST :

【 js 基础 】【 源码学习 】backbone 源码阅读(一)

最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-study)进行参考交流,有详细的源码注释,以及知识总结,同时 google 一下 backbone 源码,也有很多优秀的文章可以用来学习. 我这里主要记录一些偏设计方向的知识点.具体从以下几个方面入手:1.MVC 框架2.观察者模式 以及 控制反转 一.MVC 框架所谓 MVC 框架,包含三个部分,mod

【 js 基础 】【 源码学习 】backbone 源码阅读(二)

最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-study)进行参考交流,有详细的源码注释,以及知识总结,同时 google 一下 backbone 源码,也有很多优秀的文章可以用来学习. 我这里主要记录一些偏设计方向的知识点.这篇文章主要讲 控制反转. 一.控制反转 上篇文章有说到控制反转,但只是简略的举了个例子,在这里我们详细说一下这个知识点,它其实

Android开源框架Afinal第一篇——揭开圣女的面纱

Android开源框架Afinal第一篇——揭开圣女的面纱 分类: Android开源框架哪点事2013-09-02 14:25 260人阅读 评论(0) 收藏 举报 Afinal 这是Afinal在github的地址:https://github.com/yangfuhai/afinal Afinal这个框架主要分4块: 1.FinalDB模块:android中的orm框架,一行代码就可以进行增删改查.支持一对多,多对一等查询. 2.FinalActivity模块:android中的ioc框架

开源项目Universal Image Loader for Android 说明文档 (1) 简介

 When developing applications for Android, one often facesthe problem of displaying some graphical content from the Internet. So, youshould provide image loading from the Web in an Android app, their processingand displaying with limited memory aga

Android开源框架ViewPageIndicator和ViewPager实现Tab导航

前言: 关于使用ViewPageIndicator和ViewPager实现Tab导航,在开发社区里已经有一堆的博客对其进行了介绍,假设我还在这里写怎样去实现.那简直就是老生常谈,毫无新奇感,并且.我也不觉得自己对ViewPageIndicator的理解会比别人好,毕竟我也是看着大神的帖子.在学习实践着. 那我还写这个有啥意义呢?事实上么,就是想在这里记录下.在使用ViewPageIndicator和ViewPager实现Tab导航时,大家有可能会遇到的坑.这个坑.须要我们开发时尽量去避免的. 啥

Android开源框架 Android-Universal-Image-Loader

Android开源框架Universal-Image-Loader就像图片加载守护者,为我们提供了丰富的功能特性: (1)多线程加载图像(异步或同步): (2)高度可定制化imageloader配置(线程池.图片下载器.解码器.内存和磁盘缓存.显示图像选项等): (3)每一个显示图像有许多自定义选项(存根图片,缓存开关,解码选项,位图处理和显示等): (4)支持内存和磁盘上的图像缓存(设备的文件系统和SD卡): (5)监听加载过程(包括下载进度): 下来我们详解如何配置使用Universal-I

Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解

本篇文章继续为大家介绍Universal-Image-Loader这个开源的图片加载框架,介绍的是图片缓存策略方面的,如果大家对这个开源框架的使用还不了解,大家可以看看我之前写的一篇文章Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用,我们一般去加载大量的图片的时候,都会做缓存策略,缓存又分为内存缓存和硬盘缓存,我之前也写了几篇异步加载大量图片的文章,使用的内存缓存是LruCache这个类,LRU是Least Recently Used 近