欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解Android的内存优化--LruCache

程序员文章站 2024-02-23 11:25:58
概念: lrucache 什么是lrucache? lrucache实现原理是什么? 这两个问题其实可以作为一个问题来回答,知道了什么是 lrucache,就只然而...

概念:

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中移除掉数据,真正释放内存还是要用户手动释放。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!