详解Android的内存优化--LruCache

作者:钟光燕 时间:2022-07-18 14:28:41 

概念:

LruCache

什么是LruCache?

LruCache实现原理是什么?

这两个问题其实可以作为一个问题来回答,知道了什么是 LruCache,就只然而然的知道 LruCache 的实现原理;Lru的全称是Least Recently Used ,近期最少使用的!所以我们可以推断出 LruCache 的实现原理:把近期最少使用的数据从缓存中移除,保留使用最频繁的数据,那具体代码要怎么实现呢,我们进入到源码中看看。

LruCache源码分析


public class LruCache<K, V> {
//缓存 map 集合,为什么要用LinkedHashMap
//因为没错取了缓存值之后,都要进行排序,以确保
//下次移除的是最少使用的值
private final LinkedHashMap<K, V> map;
//当前缓存的值
private int size;
//最大值
private int maxSize;
//添加到缓存中的个数
private int putCount;
//创建的个数
private int createCount;
//被移除的个数
private int evictionCount;
//命中个数
private int hitCount;
//丢失个数
private int missCount;
//实例化 Lru,需要传入缓存的最大值
//这个最大值可以是个数,比如对象的个数,也可以是内存的大小
//比如,最大内存只能缓存5兆
public LruCache(int maxSize) {
 if (maxSize <= 0) {
  throw new IllegalArgumentException("maxSize <= 0");
 }
 this.maxSize = maxSize;
 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 = maxSize;
 }
 trimToSize(maxSize);
}
//通过 key 获取缓存值
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++;
 }
 //如果没有,用户可以去创建
 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(maxSize);
  return createdValue;
 }
}
//添加缓存,跟上面这个方法的 create 之后的代码一样的
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 += 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);
 return previous;
}
//检测缓存是否越界
private 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!");
   }
   //如果没有,则返回
   if (size <= maxSize) {
    break;
   }
   //以下代码表示已经超出了最大范围
   Map.Entry<K, V> toEvict = null;
   for (Map.Entry<K, V> entry : map.entrySet()) {
    toEvict = entry;
   }
   if (toEvict == null) {
    break;
   }
   //移除最后一个,也就是最少使用的缓存
   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");
 }
 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;
}
//这里用户可以重写它,实现数据和内存回收操作
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);
 }
 return result;
}
 //这个方法要特别注意,跟我们实例化 LruCache 的 maxSize 要呼应,怎么做到呼应呢,比如 maxSize 的大小为缓存的个数,这里就是 return 1就 ok,如果是内存的大小,如果5M,这个就不能是个数 了,这是应该是每个缓存 value 的 size 大小,如果是 Bitmap,这应该是 bitmap.getByteCount();
protected int sizeOf(K key, V value) {
 return 1;
}
//清空缓存
public final void evictAll() {
 trimToSize(-1); // -1 will evict 0-sized elements
}
public synchronized final int size() {
 return size;
}
public synchronized final int maxSize() {
 return maxSize;
}
public synchronized final int hitCount() {
 return hitCount;
}
public synchronized final int missCount() {
 return missCount;
}
public synchronized final int createCount() {
 return createCount;
}
public synchronized final int putCount() {
 return putCount;
}
public synchronized final int evictionCount() {
 return evictionCount;
}
public synchronized final Map<K, V> snapshot() {
 return new LinkedHashMap<K, V>(map);
}
}

LruCache 使用

先来看两张内存使用的图

详解Android的内存优化--LruCache

图-1

详解Android的内存优化--LruCache

图-2

以上内存分析图所分析的是同一个应用的数据,唯一不同的是图-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> mData ;
private LruCache<String,Bitmap> mLruCache ;
private int mTotalSize = (int) Runtime.getRuntime().totalMemory();
private ViewPager mViewPager ;
public LruPageAdapter(ViewPager viewPager ,List<String> data){
 mData = data ;
 mViewPager = viewPager ;
 /*实例化LruCache*/
 mLruCache = new LruCache<String,Bitmap>(mTotalSize/5){
  /*当缓存大于我们设定的最大值时,会调用这个方法,我们可以用来做内存释放操作*/
  @Override
  protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
   super.entryRemoved(evicted, key, oldValue, newValue);
   if (evicted && oldValue != null){
    oldValue.recycle();
   }
  }
  /*创建 bitmap*/
  @Override
  protected Bitmap create(String key) {
   final int resId = mViewPager.getResources().getIdentifier(key,"drawable",
     mViewPager.getContext().getPackageName()) ;
   return BitmapFactory.decodeResource(mViewPager.getResources(),resId) ;
  }
  /*获取每个 value 的大小*/
  @Override
  protected int sizeOf(String key, Bitmap value) {
   return value.getByteCount();
  }
 } ;
}
@Override
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);
 return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
 container.removeView((View) object);
}
@Override
public int getCount() {
 return mData.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
 return view == object;
}
}

总结

  • LruCache 是基于 Lru算法实现的一种缓存机制;

  • Lru算法的原理是把近期最少使用的数据给移除掉,当然前提是当前数据的量大于设定的最大值。

  • LruCache 没有真正的释放内存,只是从 Map中移除掉数据,真正释放内存还是要用户手动释放。

来源:http://blog.csdn.net/jxxfzgy/article/details/44885623

标签:Android,LruCache
0
投稿

猜你喜欢

  • C#中using指令的几种用法

    2022-01-25 01:35:25
  • c# socket网络编程接收发送数据示例代码

    2021-08-31 06:14:13
  • Java操作MongoDB模糊查询和分页查询

    2023-03-12 20:31:22
  • SpringCloud微服务基础简介

    2022-09-01 23:16:24
  • Android RadioButton 图片位置与大小实例详解

    2022-04-12 18:00:20
  • Java8新特性之接口中的默认方法和静态方法详解

    2022-05-10 15:16:19
  • c#同步两个子目录文件示例分享 两个文件夹同步

    2022-01-25 05:03:03
  • Android后端服务器的搭建方法

    2022-05-07 19:38:04
  • Java多线程之读写锁分离设计模式

    2021-06-08 07:20:48
  • Java注解之Elasticsearch的案例详解

    2022-03-30 12:28:58
  • Android简单实现弹幕效果

    2022-08-12 01:24:08
  • Android编程画图之抗锯齿解决方法

    2022-12-17 12:03:54
  • java 工厂模式的讲解及优缺点的介绍

    2022-02-05 10:57:09
  • c语言动态数组示例

    2023-11-02 22:56:44
  • Redis分布式锁实现方式及超时问题解决

    2023-08-24 23:28:34
  • 浅谈关于Java的GC垃圾回收器的一些基本概念

    2021-11-14 10:42:52
  • windows定时器配置执行java jar文件的方法详解

    2023-04-17 16:40:21
  • C# 内部类与Lambda表达式用法详解

    2022-07-13 05:54:11
  • Java IO之序列化与反序列化详解

    2023-05-21 14:59:14
  • Android中AOP的应用实践之过滤重复点击

    2022-09-19 22:01:02
  • asp之家 软件编程 m.aspxhome.com