lrucache清空缓存可以存多个类型吗

什么是LruCache?
LruCache实现原理是什么?
这两个问题其实可以作为一个问题来回答,知道了什么是 LruCache,就只然而然的知道 LruCache 的实现原理;Lru的全称是Least Recently Used ,近期最少使用的!所以我们可以推断出 LruCache 的实现原理:把近期最少使用的数据从缓存中移除,保留使用最频繁的数据,那具体代码要怎么实现呢,我们进入到源码中看看。
LruCache源码分析
public class LruCache&K, V& {
private final LinkedHashMap&K, V&
private int
private int maxS
private int putC
private int createC
private int evictionC
private int hitC
private int missC
public LruCache(int maxSize) {
if (maxSize &= 0) {
throw new IllegalArgumentException("maxSize &= 0");
this.maxSize = maxS
this.map = new LinkedHashMap&K, V&(0, 0.75f, true);
public void resize(int maxSize) {
if (maxSize &= 0) {
throw new IllegalArgumentException("maxSize &= 0");
synchronized (this) {
this.maxSize = maxS
trimToSize(maxSize);
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapV
missCount++;
V createdValue = create(key);
if (createdValue == null) {
return null;
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
if (mapValue != null) {
map.put(key, mapValue);
size += safeSizeOf(key, createdValue);
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapV
trimToSize(maxSize);
return createdV
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
if (previous != null) {
entryRemoved(false, key, previous, value);
trimToSize(maxSize);
private void trimToSize(int maxSize) {
while (true) {
synchronized (this) {
if (size & 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
if (size &= maxSize) {
Map.Entry&K, V& toEvict = null;
for (Map.Entry&K, V& entry : map.entrySet()) {
if (toEvict == null) {
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
entryRemoved(true, key, value, null);
public final V remove(K key) {
if (key == null) {
throw new NullPointerException("key == null");
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
if (previous != null) {
entryRemoved(false, key, previous, null);
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
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);
protected int sizeOf(K key, V value) {
public final void evictAll() {
trimToSize(-1);
public synchronized final int size() {
public synchronized final int maxSize() {
return maxS
public synchronized final int hitCount() {
return hitC
public synchronized final int missCount() {
return missC
public synchronized final int createCount() {
return createC
public synchronized final int putCount() {
return putC
public synchronized final int evictionCount() {
return evictionC
public synchronized final Map&K, V& snapshot() {
return new LinkedHashMap&K, V&(map);
LruCache 使用
先来看两张内存使用的图
以上内存分析图所分析的是同一个应用的数据,唯一不同的是图-1没有使用 LruCache,而图-2使用了 LruCache;可以非常明显的看到,图-1的内存使用明显偏大,基本上都是在30M左右,而图-2的内存使用情况基本上在20M左右。这就足足省了将近10M的内存!
ok,下面把实现代码贴出来
* Created by gyzhong on 15/4/5.
public class LruPageAdapter extends PagerAdapter {
private List&String& mD
private LruCache&String,Bitmap& mLruC
private int mTotalSize = (int) Runtime.getRuntime().totalMemory();
private ViewPager mViewP
public LruPageAdapter(ViewPager viewPager ,List&String& data){
mViewPager = viewP
mLruCache = new LruCache&String,Bitmap&(mTotalSize/5){
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
if (evicted && oldValue != null){
oldValue.recycle();
protected Bitmap create(String key) {
final int resId = mViewPager.getResources().getIdentifier(key,"drawable",
mViewPager.getContext().getPackageName()) ;
return BitmapFactory.decodeResource(mViewPager.getResources(),resId) ;
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
public Object instantiateItem(ViewGroup container, int position) {
View view = LayoutInflater.from(container.getContext()).inflate(R.layout.view_pager_item, null) ;
ImageView imageView = (ImageView) view.findViewById(R.id.id_view_pager_item);
Bitmap bitmap = mLruCache.get(mData.get(position));
imageView.setImageBitmap(bitmap);
container.addView(view);
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
public int getCount() {
return mData.size();
public boolean isViewFromObject(View view, Object object) {
return view ==
1、LruCache 是基于 Lru算法实现的一种缓存机制;
2、Lru算法的原理是把近期最少使用的数据给移除掉,当然前提是当前数据的量大于设定的最大值。
3、LruCache 没有真正的释放内存,只是从 Map中移除掉数据,真正释放内存还是要用户手动释放。
本文已收录于以下专栏:
相关文章推荐
前言闲扯一下,已经有好久没更新博客了,记得上一篇博客的更新时间为
15:20。截止到今天,超过一百天没更新了。这篇博客的内容大多数是从别的博客摘抄过来的,写这篇博客的目的主要是...
上篇文章讲解了使用LruCache策略在内存中缓存图片,如果你还未了解,请先看Android 缓存浅谈(一) LruCache。
     在Android应用开发中,为了提高UI的流畅性、响应速度,...
《重构》一书经典总结(一)为何重构1.重构改进软件设计
2.重构使软件更容易理解
3.重构提交稿编程速度
4.重构帮助找到bug何时重构1.三次法则
第一次做某事的时候去做,第二次会产生反感,...
《重构》一书总结(二)重构的重点,在于对那些代码进行重构,如果重构不当,反而适得其反。重构代码终结如下1.Duplicated Code 重复代码
2.Large Class 复杂的类
Android应用开发好多场景都是手机和web服务器之间进行通信,从服务端需要获取数据,但是当访问的数据比较大,比较多,并且是重复数据时,会极大影响性能,甚至应用崩溃,手机卡死,这时候就要考虑缓存机制...
package android.  
  
import java.util.LinkedHashM  
import java.util.M  
 &#160...
照片墙这种功能现在应该算是挺常见了,在很多应用中你都可以经常看到照片墙的身影。它的设计思路其实也非常简单,用一个GridView控件当作“墙”,然后随着GridView的滚动将一张张照片贴在“墙”上,...
又是好久没写博客。。
今天我们来一起学习一下缓存技术,相信大家做开发的时候都知道请求网络数据的重要,但是有一些只用请求一次就过时性的消息比如某些新闻信息,如果我们每次进入新闻界面就从新从网络上获取势...
我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状、不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小。比如说系统图片库里展示的图片大都是用手机摄像头拍出...
他的最新文章
讲师:王哲涵
讲师:韦玮
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)拒绝访问 |
| 百度云加速
请打开cookies.
此网站 () 的管理员禁止了您的访问。原因是您的访问包含了非浏览器特征(3adac-ua98).
重新安装浏览器,或使用别的浏览器在Android中,有一个叫做LruCache类专门用来做图片缓存处理的。
它有一个特点,当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉。
我们来看看它的源码(注意是在android.support.v4.util下):
package android.support.v4.
import java.util.LinkedHashM
import java.util.M
public class LruCache&K, V& {
private final LinkedHashMap&K, V&
private int maxS
private int putC
private int createC
private int evictionC
private int hitC
private int missC
* 构造方法
maxSize 缓存的最大值
public LruCache(int maxSize) {
if (maxSize &= 0) {
throw new IllegalArgumentException("maxSize &= 0");
this.maxSize = maxS
this.map = new LinkedHashMap&K, V&(0, 0.75f, true);
* 重新设置最大缓存大小
public void resize(int maxSize) {
if (maxSize &= 0) {
throw new IllegalArgumentException("maxSize &= 0");
synchronized (this) {
this.maxSize = maxS
trimToSize(maxSize);
通过key获取相应的value,或者创建返回相应的value。相应的value会移动到队列的头部,
如果item的value没有被cache或者不能被创建,则返回null。
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapV
missCount++;
V createdValue = create(key);
if (createdValue == null) {
return null;
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
if (mapValue != null) {
map.put(key, mapValue);
size += safeSizeOf(key, createdValue);
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapV
trimToSize(maxSize);
return createdV
* 在缓存集合(LinkedHashMap)队首添加value
the previous value mapped by { key}.
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
if (previous != null) {
entryRemoved(false, key, previous, value);
trimToSize(maxSize);
* 调整cache空间
* 当缓存的大小超过原来设定的maxSize时,遍历map,将多余的项(代码中对应toEvict)剔除掉,直到当
* 前cache的大小等于或小于限定的大小
maxSize the maximum size of the cache before returning. May be -1
to evict even 0-sized elements.
public void trimToSize(int maxSize) {
while (true) {
synchronized (this) {
if (size & 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
if (size &= maxSize || map.isEmpty()) {
Map.Entry&K, V& toEvict = map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
entryRemoved(true, key, value, null);
* 从缓存中移除掉key对应的value
the previous value mapped by { key}.
public final V remove(K key) {
if (key == null) {
throw new NullPointerException("key == null");
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
if (previous != null) {
entryRemoved(false, key, previous, null);
* 当item被回收或者删掉时调用。该方法当value被回收释放存储空间时被remove调用,
* 或者替换item值时put调用,默认实现什么都没做。
evicted true if the entry is being removed to make space, false
if the removal was caused by a { #put} or { #remove}.
newValue the new value for { key}, if it exists. If non-null,
this removal was caused by a { #put}. Otherwise it was caused by
an eviction or a { #remove}.
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
* 当某Item丢失时会调用到,返回计算的相应的value或者null
* &p&If a value for { key} exists in the cache when this method
* returns, the created value will be released with { #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 { #put} while another is creating a value for the same
protected V create(K key) {
return null;
* 计算某个item的大小,如果是Bitmap,这应该是bitmap.getByteCount();
private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result & 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
protected int sizeOf(K key, V value) {
* 清空缓存
public final void evictAll() {
trimToSize(-1);
* For caches that do not override { #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() {
* For caches that do not override { #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 maxS
* Returns the number of times { #get} returned a value that was
* already present in the cache.
public synchronized final int hitCount() {
return hitC
* 返回get()为null的次数或者创建的次数
public synchronized final int missCount() {
return missC
* 返回创建create()的次数
public synchronized final int createCount() {
return createC
* 返回put的次数
public synchronized final int putCount() {
return putC
* 返回被回收的数量
public synchronized final int evictionCount() {
return evictionC
* 返回当前cache的副本,从最近最少访问到最多访问
public synchronized final Map&K, V& snapshot() {
return new LinkedHashMap&K, V&(map);
@Override public synchronized final String toString() {
int accesses = hitCount + missC
int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
maxSize, hitCount, missCount, hitPercent);
我们来分析下LinkedHashMap:
LinkedHashMap中的get()方法不仅返回所匹配的值,并且在返回前还会将所匹配的key对应的entry调整在列表中的顺序(LinkedHashMap使用双链表来保存数据),让它处于链表的最后。当然,这种情况必须是在LinkedHashMap中accessOrder==true的情况下才生效的,反之就是get()方法不会改变被匹配的key对应的entry在列表中的位置,这样将大大方便了缓存顺序的调整和缓存的移除。这就是为什么要使用LinkedHashMap的原因。
public V get(Object key) {
if (key == null) {
HashMapEntry&K, V& e = entryForNullK
if (e == null)
return null;
if (accessOrder)
makeTail((LinkedEntry&K, V&) e);
return e.value;
int hash = Collections.secondaryHash(key);
HashMapEntry&K, V&[] tab =
for (HashMapEntry&K, V& e = tab[hash & (tab.length - 1)];
e != null; e = e.next) {
K eKey = e.
if (eKey == key || (e.hash == hash && key.equals(eKey))) {
if (accessOrder)
makeTail((LinkedEntry&K, V&) e);
return e.value;
return null;
好了,LruCache就介绍到这里,下面有个使用例子,大家可以看看:
package com.king.imagechooser.
import android.graphics.B
import android.graphics.BitmapF
import android.os.H
import android.os.L
import android.os.M
import android.util.DisplayM
import android.util.L
import android.util.LruC
import android.view.ViewGroup.LayoutP
import android.widget.ImageV
import java.lang.reflect.F
import java.util.LinkedL
import java.util.concurrent.ExecutorS
import java.util.concurrent.E
import java.util.concurrent.S
* 本地图片加载框架(读取本地图片)
* Created by King on .
public class ImageLoader {
* 图片缓存
private LruCache&String, Bitmap& mC
private ExecutorService mThreadP
* 默认线程数量
private final static int DEFAULT_THREAD_COUNTS = 1;
private Semaphore mSemaphore = new Semaphore(0);
private Semaphore mSemaphoreThreadP
private Type mType = Type.LIFO;
public enum Type {
FIFO, LIFO
private LinkedList&Runnable& mTaskQ
private Thread mPoolT
private Handler mPoolThreadH
private Handler mUIH
private static ImageLoader mI
private ImageLoader(int treadCount, Type type) {
init(treadCount, type);
public static ImageLoader getInstance(int treadCount, Type type) {
if (mInstance == null) {
synchronized (ImageLoader.class) {
if (mInstance == null) {
mInstance = new ImageLoader(treadCount, type);
* threadCount
public void init(int threadCount, Type type) {
mPoolThread = new Thread() {
public void run() {
Looper.prepare();
mPoolThreadHandler = new Handler() {
public void handleMessage(Message msg) {
mThreadPool.execute(getTaskThread());
mSemaphoreThreadPool.acquire();
} catch (Exception e) {
Log.e("King", e.getMessage());
mSemaphore.release();
Looper.loop();
mPoolThread.start();
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheMemory = maxMemory / 4;
mCaches = new LruCache&String, Bitmap&(cacheMemory) {
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
mThreadPool = Executors.newFixedThreadPool(threadCount);
mTaskQueue = new LinkedList&Runnable&();
mSemaphoreThreadPool = new Semaphore(threadCount);
* 显示图片到ImageView
public void loadImage(final String path, final ImageView imageView) {
imageView.setTag(path);
if (mUIHandler == null) {
mUIHandler = new Handler() {
public void handleMessage(Message msg) {
ImageHolder holder = (ImageHolder) msg.
Bitmap bm = holder.
ImageView iv = holder.imageV
String path = holder.
if (iv.getTag().toString().equals(path)) {
iv.setImageBitmap(bm);
Bitmap bitmap = getBitmapFromCache(path);
if (bitmap != null) {
refreshBitmap(path, imageView, bitmap);
addTaskThread(new Runnable() {
public void run() {
ImageSize imageSize = getImageViewSize(imageView);
Bitmap bitmap = decodeBitmapFromPath(path, imageSize.width, imageSize.height);
addBitmapToCache(path, bitmap);
refreshBitmap(path, imageView, bitmap);
mSemaphoreThreadPool.release();
private void refreshBitmap(String path, ImageView imageView, Bitmap bitmap) {
ImageHolder holder = new ImageHolder();
holder.bitmap =
holder.imageView = imageV
holder.path =
Message msg = Message.obtain();
mUIHandler.sendMessage(msg);
* 获取ImageView的实际大小
protected ImageSize getImageViewSize(ImageView imageView) {
ImageSize size = new ImageSize();
DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();
LayoutParams lp = imageView.getLayoutParams();
int width = imageView.getWidth();
if (width &= 0) {
width = lp.
if (width &= 0) {
width = getImageViewFieldValue(imageView, "mMaxWidth");
if (width &= 0) {
width = displayMetrics.widthP
int height = imageView.getHeight();
if (height &= 0) {
height = lp.
if (height &= 0) {
height = getImageViewFieldValue(imageView, "mMaxHeight");
if (height &= 0) {
height = displayMetrics.heightP
size.width =
size.height =
private static int getImageViewFieldValue(Object object, String fieldName) {
int value = 0;
Field field = ImageView.class.getDeclaredField(fieldName);
field.setAccessible(true);
int fieldValue = field.getInt(object);
if (fieldValue & 0 && fieldValue & Integer.MAX_VALUE) {
value = fieldV
} catch (NoSuchFieldException e) {
Log.e("King", e.getMessage());
} catch (IllegalAccessException e) {
e.printStackTrace();
* 压缩图片
protected Bitmap decodeBitmapFromPath(String path, int width, int height) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = calculateInSampleSize(options, width, height);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
} catch (Throwable tr) {
Log.e("King", tr.getMessage());
return null;
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
final int height = options.outH
final int width = options.outW
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 = Math.max(heightRatio, widthRatio);
return inSampleS
} catch (Throwable e) {
Log.e("King", e.getMessage());
* 将图片添加到缓存
private void addBitmapToCache(String path, Bitmap bm) {
if (getBitmapFromCache(path) == null) {
if (bm != null) {
mCaches.put(path, bm);
private synchronized void addTaskThread(Runnable runnable) {
mTaskQueue.add(runnable);
if (mPoolThreadHandler == null) {
mSemaphore.acquire();
mPoolThreadHandler.sendEmptyMessage(0x110);
} catch (Throwable tr) {
Log.e("King", tr.getMessage(), tr);
* 获取线程
private Runnable getTaskThread() {
if (mType == Type.FIFO) {
return mTaskQueue.removeFirst();
} else if (mType == Type.LIFO) {
return mTaskQueue.removeLast();
return null;
* 从缓存中获取图片
public Bitmap getBitmapFromCache(String key) {
return mCaches.get(key);
private class ImageHolder {
ImageView imageV
private class ImageSize {
本文已收录于以下专栏:
相关文章推荐
在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量。对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理...
一、LruCache
LRU:Least Recently Used(近期最少使用)。LruCache基于LRU算法的缓存策略。LruCache是一个泛型类,其以强引用的方式存储外界的缓存对象。当...
缓存策略在移动端设备上是非常重要的,尤其是在图片加载这个场景下,因为图片相对而言比较大会花费用户较多的流量,因此可用缓存方式来解决,即当程序第一次从网络上获取图片的时候,就将其缓存到存储设备上,这样在...
说说下拉刷新:
下拉刷新头文件的布局:pulllist-head.xml
首次登录进来SQLite里面没有数据,需手动点击请求网络加载:
之后就可以下拉刷新了:
    android:...
如下是一个LruCache 来缓存图片的例子:1.private LruCache mMemoryC
3.@Override
4.protected void onCrea...
他的最新文章
讲师:王哲涵
讲师:韦玮
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 redis存储类型 的文章

 

随机推荐