LruCache的使用及原理
/**
* Static library version of {@link android.util.LruCache}. Used to write apps
* that run on API levels prior to 12. When running on API level 12 or above,
* this implementation is still used; it does not try to switch to the
* framework's implementation. See the framework SDK documentation for a class
* overview.
*/
public class LruCache<K, V> {
/**缓存 map 集合,要用LinkedHashMap */
private final LinkedHashMap<K, V> map; /**缓存大小 */
private int size;
/**最大缓存大小*/
private int maxSize;
/**put的次数*/
private int putCount;
/**create的次数*/
private int createCount;
/**回收的次数*/
private int evictionCount;
/**命中的次数*/
private int hitCount;
/**丢失的次数*/
private int missCount; /**
*构造方法,maxSize最大缓存大小,初始化LinkedHashMap
*/
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
//将LinkedHashMap的accessOrder设置为true来实现LRU
//false 插入顺序
//true 访问顺序
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
} /**
* 重新设置最大缓存大小
* @param maxSize 最大缓存大小.
*/
public void resize(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
} synchronized (this) {
this.maxSize = maxSize;
}
trimToSize(maxSize);
} /**
如果缓存中存在或者被创建过,返回该值,如果已经返回了,会被移动到队列头部,如果没有被缓存和创建,会被返回null
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
} V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
} /*
*如果丢失了就试图创建一个item
*/ V createdValue = create(key);
if (createdValue == null) {
return null;
} synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue); if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
} if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
//每次新加入对象都需要调用trimToSize方法看是否需要回收
trimToSize(maxSize);
return createdValue;
}
} /**
*会被移动到队列头部
* @return the previous value mapped by {@code key}.
*/
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
} V previous;
synchronized (this) {
putCount++;
//size加上预put对象的大小
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
//如果之前存在键为key的对象,则size应该减去原来对象的大小
size -= safeSizeOf(key, previous);
}
} if (previous != null) {
entryRemoved(false, key, previous, value);
}
//每次新加入对象都需要调用trimToSize方法看是否需要回收
trimToSize(maxSize);
return previous;
} /**
* 删除最老的条目,直到其余条目的总数达到或低于要求的大小。
*/
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
//如果当前size小于maxSize或者map没有任何对象,则结束循环
if (size <= maxSize || map.isEmpty()) {
break;
}
//移除链表头部的元素,并进入下一次循环
Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
//回收次数+1
evictionCount++;
} entryRemoved(true, key, value, null);
}
} /**
*从内存缓存中根据key值移除某个对象并返回该对象
*/
public final V remove(K key) {
if (key == null) {
throw new NullPointerException("key == null");
} V previous;
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
} if (previous != null) {
entryRemoved(false, key, previous, null);
} return previous;
} /**
* Called for entries that have been evicted or removed. This method is
* invoked when a value is evicted to make space, removed by a call to
* {@link #remove}, or replaced by a call to {@link #put}. The default
* implementation does nothing.
*当item被回收或者删掉时调用。改方法当value被回收释放存储空间时被remove调用,
* 或者替换item值时put调用,默认实现什么都没做
* <p>The method is called without synchronization: other threads may
* access the cache while this method is executing.
*
* @param evicted true if the entry is being removed to make space, false
* if the removal was caused by a {@link #put} or {@link #remove}.
* true---为释放空间被删除;false---put或remove导致
* @param newValue the new value for {@code key}, if it exists. If non-null,
* this removal was caused by a {@link #put}. Otherwise it was caused by
* an eviction or a {@link #remove}.
*/
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {} /**
* Called after a cache miss to compute a value for the corresponding key.
* Returns the computed value or null if no value can be computed. The
* default implementation returns null.
*
* <p>The method is called without synchronization: other threads may
* access the cache while this method is executing.
*
* <p>If a value for {@code key} exists in the cache when this method
* returns, the created value will be released with {@link #entryRemoved}
* and discarded. This can occur when multiple threads request the same key
* at the same time (causing multiple values to be created), or when one
* thread calls {@link #put} while another is creating a value for the same
* key.
* 当某Item丢失时会调用到,返回计算的相应的value或者null
*/
protected V create(K key) {
return null;
} private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
}
return result;
} /**
* Returns the size of the entry for {@code key} and {@code value} in
* user-defined units. The default implementation returns 1 so that size
* is the number of entries and max size is the maximum number of entries.
*
* <p>An entry's size must not change while it is in the cache.
*这个方法要特别注意,跟我们实例化LruCache的maxSize要呼应,怎么做到呼应呢,比如maxSize的大小为缓存
*的个数,这里就是return 1就ok,如果是内存的大小,如果5M,这个就不能是个数了,就需要覆盖这个方法,返回每个缓存
*value的size大小,如果是Bitmap,这应该是bitmap.getByteCount();
*/
protected int sizeOf(K key, V value) {
return 1;
} /**
* Clear the cache, calling {@link #entryRemoved} on each removed entry.
* 清理缓存
*/
public final void evictAll() {
trimToSize(-1); // -1 will evict 0-sized elements
} /**
* For caches that do not override {@link #sizeOf}, this returns the number
* of entries in the cache. For all other caches, this returns the sum of
* the sizes of the entries in this cache.
* 缓存大小
*/
public synchronized final int size() {
return size;
} /**
* For caches that do not override {@link #sizeOf}, this returns the maximum
* number of entries in the cache. For all other caches, this returns the
* maximum sum of the sizes of the entries in this cache.
* 缓存最大值
*/
public synchronized final int maxSize() {
return maxSize;
} /**
* Returns the number of times {@link #get} returned a value that was
* already present in the cache.
*返回{@link#get}返回值的次数,该值为
*已存在于缓存中。
*/
public synchronized final int hitCount() {
return hitCount;
} /**
* Returns the number of times {@link #get} returned null or required a new
* value to be created.
*返回创建或者返回null的次数
*/
public synchronized final int missCount() {
return missCount;
} /**
* Returns the number of times {@link #create(Object)} returned a value.
返回创建一个元素的次数
*/
public synchronized final int createCount() {
return createCount;
} /**
* Returns the number of times {@link #put} was called.
调用put返回次数
*/
public synchronized final int putCount() {
return putCount;
} /**
* Returns the number of values that have been evicted.
* 返回已被逐出的值的数目。
*/
public synchronized final int evictionCount() {
return evictionCount;
} /**
* Returns a copy of the current contents of the cache, ordered from least
* recently accessed to most recently accessed.
* 返回缓存拷贝,排序规则为最近最多访问
*/
public synchronized final Map<K, V> snapshot() {
return new LinkedHashMap<K, V>(map);
} /**
* 返回maxSize hits misses hitRate值
* LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%
*/
@Override public synchronized final String toString() {
int accesses = hitCount + missCount;
int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
maxSize, hitCount, missCount, hitPercent);
}
}
实际使用:
public class BitmapCacheActivity extends Activity {
    private ImageView iv_picture;
    private BitmapCache<String, Bitmap> mMemoryCache;
    private BitmapCache.BitmapRemovedCallBack<String> mEnteryRemovedCallBack =
            new BitmapCache.BitmapRemovedCallBack<String>() {
                @Override
                public void onBitmapRemoved(String key) {
                    //处理回收bitmap前,清空相关view的bitmap操作
                    mMemoryCache.remove(key);
                }
            };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bitmapcache);
        iv_picture = findViewById(R.id.iv_picture);
// 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。
        // BitmapCache通过构造函数传入缓存值,以bit为单位。
        int memClass = ((ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
        // 使用单个应用最大可用内存值的1/8作为缓存的大小。
        int cacheSize = 1024 * 1024 * memClass / 8;
        mMemoryCache = new BitmapCache<>(cacheSize, mEnteryRemovedCallBack);
        loadBitmap(R.mipmap.ic_launcher_round, iv_picture);
    }
    /**
     * bitmap添加到缓存中去
     * @param key
     * @param bitmap
     */
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }
    /**
     * 从缓存中获取bitmap
     * @param key
     * @return
     */
    public Bitmap getBitmapFromMemCache(String key) {
        return mMemoryCache.get(key);
    }
    /**
     * 加载bitmap
     * @param resId
     * @param imageView
     */
    public void loadBitmap(int resId, ImageView imageView) {
        final String imageKey = String.valueOf(resId);
        final Bitmap bitmap = getBitmapFromMemCache(imageKey);
        //从缓存里面获取,没有设置默认的,然后在进行一步缓存操作
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        } else {
            imageView.setImageResource(R.drawable.person_image_empty);
            BitmapLoadingTask task = new BitmapLoadingTask(imageView);
            task.execute(resId);
        }
    }
    class BitmapLoadingTask extends AsyncTask<Integer, Void, Bitmap> {
        private ImageView imageView;
        public BitmapLoadingTask(ImageView imageView) {
            this.imageView = imageView;
        }
        // 在后台加载图片。
        @Override
        protected Bitmap doInBackground(Integer... params) {
            final Bitmap bitmap = decodeSampledBitmapFromResource(
                    getResources(), params[0], 100, 100);
            addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
            return bitmap;
        }
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //显示在bitmap上
            imageView.setImageBitmap(bitmap);
        }
        public Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                      int reqWidth, int reqHeight) {
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(res, resId, options);
            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeResource(res, resId, options);
        }
        public int calculateInSampleSize(
                BitmapFactory.Options options, int reqWidth, int reqHeight) {
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
            if (height > reqHeight || width > reqWidth) {
                final int heightRatio = Math.round((float) height / (float) reqHeight);
                final int widthRatio = Math.round((float) width / (float) reqWidth);
                inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
            }
            return inSampleSize;
        }
    }
}
LruCache的使用及原理的更多相关文章
- android基础---->LruCache的使用及原理
		
在你应用程序的UI界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来.Android为我们提供了LruCache,今天我们就来学习这个缓存的知识以及原理. ...
 - android基础---->DiskLruCache的使用及原理
		
DiskLruCache是谷歌推荐的用来实现硬盘缓存的类,今天我们开始对于DiskLruCache的学习.DiskLruCache的测试代码:DiskLruCache的测试代码下载.关于FidkLru ...
 - Android常用库源码解析
		
图片加载框架比较 共同优点 都对多级缓存.线程池.缓存算法做了处理 自适应程度高,根据系统性能初始化缓存配置.系统信息变更后动态调整策略.比如根据 CPU 核数确定最大并发数,根据可用内存确定内存缓存 ...
 - LruCache算法原理及实现
		
LruCache算法原理及实现 LruCache算法原理 LRU为Least Recently Used的缩写,意思也就是近期最少使用算法.LruCache将LinkedHashMap的顺序设置为LR ...
 - 内存缓存LruCache实现原理
		
自己项目中一直都是用的开源的xUtils框架,包括BitmapUtils.DbUtils.ViewUtils和HttpUtils四大模块,这四大模块都是项目中比较常用的.最近决定研究一下xUtils的 ...
 - LruCache原理解析
		
LruCache是一个泛型类,它内部采用LinkedHashMap,并以强引用的方式存储外界的缓存对象,提供get和put方法来完成缓存的获取和添加操作.当缓存满时,LruCache会移除较早的缓存对 ...
 - LRUCache原理分析
		
一.注释 LRUCache的原理,基本都在注释里面描述清楚了. /** * A cache that holds strong references to a limited number of va ...
 - Android LruCache技术原理
		
概述 记得在很早之前,我有写过一篇文章Android高效加载大图.多图解决方案,有效避免程序OOM,这篇文章是翻译自Android Doc的,其中防止多图OOM的核心解决思路就是使用LruCache技 ...
 - Android面试收集录10 LruCache原理解析
		
一.Android中的缓存策略 一般来说,缓存策略主要包含缓存的添加.获取和删除这三类操作.如何添加和获取缓存这个比较好理解,那么为什么还要删除缓存呢?这是因为不管是内存缓存还是硬盘缓存,它们的缓存大 ...
 
随机推荐
- ffmpeg.c函数结构简单分析(画图)
			
前一阵子研究转码的时候看了FFmpeg的源代码.由于ffmpeg.c的代码相对比较长,而且其中有相当一部分是AVFilter有关的代码(这一部分一直不太熟),因此之前学习FFmpeg的时候一直也没有好 ...
 - MariaDB存储引擎
			
MariaDB存储引擎 存储引擎就是指表的类型.数据库的存储引擎决定了表在计算机中的存储方式.存储引擎的概念是MariaDB的特点,而且是一种插入式的存储引擎概念.这决定了MariaDB数据库中的表可 ...
 - 摆脱命令行,Ubuntu下配置Android开发环境
			
作者:宋志辉 微博:weibo.com/askzhihui/ 安装所需工具 一.新立得 新立得(Synaptic)是Debian和Ubuntu Linux操作系统的包管理工具apt的图形化前端. 它结 ...
 - Unity2D Sprite灰白图(Unity3D开发之十一)
			
猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=596 昨晚看到群里问到2DSpr ...
 - C++ Primer 有感(重载操作符)
			
1.用于内置类型的操作符,其含义不能改变.也不能为任何内置类型定义额外的新的操作符.(重载操作符必须具有至少一个类类型或枚举类型的操作数.这条规则强制重载操作符不能重新定义用于内置类型对象的操作符的含 ...
 - Google的两种广告推广方式
			
1搜索关键字广告推送:AdWords: 覆盖广泛:在全球最大的搜索和网络平台上进行推广. 定位精准:锁定目标客户群体,让潜在客户轻松找上门. 成本可控:仅当用户点击广告时,您才支付费用. 2.网站内容 ...
 - Spring揭秘 读书笔记 七 BeanFactory的启动分析
			
首先,先看我自己画的BeanFactory启动时的时序图. 第一次接触时序图,可能有些地方画的不是很符合时序图的规则,大家只关注调用顺序即可. public static void main(Stri ...
 - Android高级控件(五)——如何打造一个企业级应用对话列表,以QQ,微信为例
			
Android高级控件(五)--如何打造一个企业级应用对话列表,以QQ,微信为例 看标题这么高大上,实际上,还是运用我么拿到listview去扩展,我们讲什么呢,就是研究一下QQ,微信的这种对话列表, ...
 - 【翻译】Ext JS 5.0.1 中的新功能
			
原文:What's New in Ext JS 5.0.1 今天,我们很高兴的宣布Ext JS 5.0.1发布了!此维护版本基于Sencha社区的反馈做了一些改进.下面让我们来了解一下这些改变. 可访 ...
 - 添加启动游戏过渡场景Default Splash Scene(Unity3D开发之十三)
			
添加启动游戏过渡场景Default Splash Scene(Unity3D开发之十三) 猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blo ...