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

Android图片缓存之Lru算法(二)

程序员文章站 2024-03-06 17:04:44
前言: 上篇我们总结了bitmap的处理,同时对比了各种处理的效率以及对内存占用大小,。我们得知一个应用如果使用大量图片就会导致oom(out of memory),那该...

前言:
上篇我们总结了bitmap的处理,同时对比了各种处理的效率以及对内存占用大小,。我们得知一个应用如果使用大量图片就会导致oom(out of memory),那该如何处理才能近可能的降低oom发生的概率呢?之前我们一直在使用softreference软引用,softreference是一种现在已经不再推荐使用的方式,因为从 android 2.3 (api level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用变得不再可靠,所以今天我们来认识一种新的缓存处理算法lru,然后学习一下基于lru的lrucache、disklrucache 实现我们的图片缓存。 

lru:
lru是least recently used 的缩写,翻译过来就是“最近最少使用”,lru缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条,那怎么确定删除哪条过期数据呢,采用lru算法实现的话就是将最老的数据删掉。

基于lrucache实现内存缓存:
1.)初始化memorycache
这里内存缓存的是drawable 而不是bitmap 理由是drawable相对bitmap来说有很大的内存优势        

 int maxmemory = (int) runtime.getruntime().maxmemory();//获取系统分配给应用的总内存大小
 int mcachesize = maxmemory / 8;//设置图片内存缓存占用八分之一
 mmemorycache = new lrucache<string, drawable>(mcachesize) {
  //必须重写此方法,来测量bitmap的大小
  @override
  protected int sizeof(string key, drawable value) {
  if (value instanceof bitmapdrawable) {
   bitmap bitmap = ((bitmapdrawable) value).getbitmap();
   return bitmap == null ? 0 : bitmap.getbytecount();
  }
  return super.sizeof(key, value);
  }
 };

2.)添加一个drawable到内存缓存 

 /**
 * 添加drawable到内存缓存
 *
 * @param key
 * @param drawable
 */
 private void adddrawabletomemorycache(string key, drawable drawable) {
 if (getdrawablefrommemcache(key) == null && drawable != null) {
  mmemorycache.put(key, drawable);
 }
 }

3.)从内存缓存中获取一个drawable

 /**
 * 从内存缓存中获取一个drawable
 *
 * @param key
 * @return
 */
 public drawable getdrawablefrommemcache(string key) {
 return mmemorycache.get(key);
 }

4.)从内存缓存中移除一个drawable

 /**
 * 从内存缓存中移除
 *
 * @param key
 */
 public void removecachefrommemory(string key) {
 mmemorycache.remove(key);
 }

5.)清空内存缓存

 /**
 * 清理内存缓存
 */
 public void cleanmemoryccache() {
 mmemorycache.evictall();
 } 

其实lru缓存机制本质上就是存储在一个linkedhashmap存储,为了保障插入的数据顺序,方便清理。 

基于disklrucache实现磁盘缓存:
disklrucache类并不是谷歌官方实现,需要自行下载,下载地址:https://github.com/jakewharton/disklrucache

1.)初始化disklrucache 

 file cachedir = context.getcachedir();//指定的是数据的缓存地址
 long diskcachesize = 1024 * 1024 * 30;//最多可以缓存多少字节的数据
 int appversion = disklruutils.getappversion(context);//指定当前应用程序的版本号
 int valuecount = 1;//指定同一个key可以对应多少个缓存文件
 try {
  mdiskcache = disklrucache.open(cachedir, appversion, valuecount, diskcachesize);
 } catch (exception ex) {
 }

2.)写入一个文件到磁盘缓存 

 /**
 * 添加bitmap到磁盘缓存
 *
 * @param key
 * @param value
 */
 private void addbitmaptodiskcache(string key, byte[] value) {
 outputstream out = null;
 try {
  disklrucache.editor editor = mdiskcache.edit(key);
  if (editor != null) {
  out = editor.newoutputstream(0);
  if (value != null && value.length > 0) {
   out.write(value);
   out.flush();
   editor.commit();
  } else {
   editor.abort();
  }
  }
  mdiskcache.flush();
 } catch (ioexception e) {
  e.printstacktrace();
 } finally {
  disklruutils.closequietly(out);
 }
 }

3.)从磁盘缓存中读取drawable 

 /**
 * 从磁盘缓存中获取一个drawable
 *
 * @param key
 * @return
 */
 public drawable getdrawablefromdiskcache(string key) {
 try {
  disklrucache.snapshot snapshot = mdiskcache.get(key);
  if (snapshot != null) {
  inputstream is = snapshot.getinputstream(0);
  bitmap bitmap = bitmapfactory.decodestream(is);
  drawable drawable = disklruutils.bitmap2drawable(bitmap);
  //从磁盘中读取到之后 加入内存缓存
  adddrawabletomemorycache(key, drawable);
  return drawable;
  }
 } catch (ioexception e) {
  e.printstacktrace();
 }
 return null;
 }

4.)从磁盘缓存中移除

 /**
 * 从磁盘缓存中移除
 *
 * @param key
 */
 public void removecachefromdisk(string key) {
 try {
  mdiskcache.remove(key);
 } catch (exception e) {
 }
 }

5.)清空磁盘缓存 

 /**
 * 清理磁盘缓存
 */
 public void cleandiskcache() {
 try {
  mdiskcache.delete();
 } catch (exception e) {
 }
 }


图片下载过程:
接下来实例中用到了一点rxjava的知识有不了解rxjava的请自行了解一下。 
1.)采用异步方式操作磁盘缓存和网络下载, 内存缓存可以在主线程中操作 

 public void display(final imageview imageview, string imageurl) {
  //生成唯一key
  final string key = disklruutils.hashkeyfordisk(imageurl);
  //先从内存中读取
  drawable drawablefrommemcache = getdrawablefrommemcache(key);
  if (drawablefrommemcache != null) {
   imageview.setimagedrawable(drawablefrommemcache);
   return;
  }
  observable.just(imageurl)
    .map(new func1<string, drawable>() {
     @override
     public drawable call(string imageurl) { // 参数类型 string
      //从磁盘中读取
      drawable drawablefromdiskcache = getdrawablefromdiskcache(key);
      if (drawablefromdiskcache != null) {
       return drawablefromdiskcache;
      }
      //网络下载
      return download(imageurl); // 返回类型 drawable
     }
    })
    .subscribeon(schedulers.io()) // 指定 subscribe() 发生在 io 线程
    .observeon(androidschedulers.mainthread()) // 指定 subscriber 的回调发生在主线程
    .subscribe(new action1<drawable>() {
     @override
     public void call(drawable drawable) { // 参数类型 drawable
      imageview.setimagedrawable(drawable);
     }
    });
 }

2.)下载图片过程以及处理 

 private drawable download(string imageurl) {
  httpurlconnection urlconnection = null;
  bytearrayoutputstream bos = null;
  inputstream ins = null;
  try {
   final url url = new url(imageurl);
   urlconnection = (httpurlconnection) url.openconnection();
   ins = urlconnection.getinputstream();
   bos = new bytearrayoutputstream();
   int b;
   while ((b = ins.read()) != -1) {
    bos.write(b);
   }
   bos.flush();
   byte[] bytes = bos.tobytearray();
   bitmap bitmap = disklruutils.bytes2bitmap(bytes);
   string key = disklruutils.hashkeyfordisk(imageurl);
   drawable drawable = disklruutils.bitmap2drawable(bitmap);
   //加入内存缓存
   adddrawabletomemorycache(key, drawable);
   //加入磁盘缓存
   addbitmaptodiskcache(key, bytes);
   return drawable;
  } catch (ioexception e) {
   e.printstacktrace();
  } finally {
   if (urlconnection != null) {
    urlconnection.disconnect();
   }
   disklruutils.closequietly(bos);
   disklruutils.closequietly(ins);
  }
  return null;
 }

附上最终图片缓存单例简单实现全部代码以及disklruutils工具类代码
 imageloadmanager.java

public class imageloadmanager {
 private lrucache<string, drawable> mmemorycache;//内存缓存
 private disklrucache mdiskcache;//磁盘缓存
 private static imageloadmanager minstance;//获取图片下载单例引用

 /**
  * 构造器
  *
  * @param context
  */
 private imageloadmanager(context context) {
  int maxmemory = (int) runtime.getruntime().maxmemory();//获取系统分配给应用的总内存大小
  int mcachesize = maxmemory / 8;//设置图片内存缓存占用八分之一
  mmemorycache = new lrucache<string, drawable>(mcachesize) {
   //必须重写此方法,来测量bitmap的大小
   @override
   protected int sizeof(string key, drawable value) {
    if (value instanceof bitmapdrawable) {
     bitmap bitmap = ((bitmapdrawable) value).getbitmap();
     return bitmap == null ? 0 : bitmap.getbytecount();
    }
    return super.sizeof(key, value);
   }
  };

  file cachedir = context.getcachedir();//指定的是数据的缓存地址
  long diskcachesize = 1024 * 1024 * 30;//最多可以缓存多少字节的数据
  int appversion = disklruutils.getappversion(context);//指定当前应用程序的版本号
  int valuecount = 1;//指定同一个key可以对应多少个缓存文件
  try {
   mdiskcache = disklrucache.open(cachedir, appversion, valuecount, diskcachesize);
  } catch (exception ex) {
  }
 }

 /**
  * 获取单例引用
  *
  * @return
  */
 public static imageloadmanager getinstance(context context) {
  imageloadmanager inst = minstance;
  if (inst == null) {
   synchronized (requestmanager.class) {
    inst = minstance;
    if (inst == null) {
     inst = new imageloadmanager(context.getapplicationcontext());
     minstance = inst;
    }
   }
  }
  return inst;
 }

 public void display(final imageview imageview, string imageurl) {
  //生成唯一key
  final string key = disklruutils.hashkeyfordisk(imageurl);
  //先从内存中读取
  drawable drawablefrommemcache = getdrawablefrommemcache(key);
  if (drawablefrommemcache != null) {
   imageview.setimagedrawable(drawablefrommemcache);
   return;
  }
  observable.just(imageurl)
    .map(new func1<string, drawable>() {
     @override
     public drawable call(string imageurl) { // 参数类型 string
      //从磁盘中读取
      drawable drawablefromdiskcache = getdrawablefromdiskcache(key);
      if (drawablefromdiskcache != null) {
       return drawablefromdiskcache;
      }
      //网络下载
      return download(imageurl); // 返回类型 drawable
     }
    })
    .subscribeon(schedulers.io()) // 指定 subscribe() 发生在 io 线程
    .observeon(androidschedulers.mainthread()) // 指定 subscriber 的回调发生在主线程
    .subscribe(new action1<drawable>() {
     @override
     public void call(drawable drawable) { // 参数类型 drawable
      imageview.setimagedrawable(drawable);
     }
    });
 }


 /**
  * 添加drawable到内存缓存
  *
  * @param key
  * @param drawable
  */
 private void adddrawabletomemorycache(string key, drawable drawable) {
  if (getdrawablefrommemcache(key) == null && drawable != null) {
   mmemorycache.put(key, drawable);
  }
 }

 /**
  * 从内存缓存中获取一个drawable
  *
  * @param key
  * @return
  */
 public drawable getdrawablefrommemcache(string key) {
  return mmemorycache.get(key);
 }

 /**
  * 从磁盘缓存中获取一个drawable
  *
  * @param key
  * @return
  */
 public drawable getdrawablefromdiskcache(string key) {
  try {
   disklrucache.snapshot snapshot = mdiskcache.get(key);
   if (snapshot != null) {
    inputstream is = snapshot.getinputstream(0);
    bitmap bitmap = bitmapfactory.decodestream(is);
    drawable drawable = disklruutils.bitmap2drawable(bitmap);
    //从磁盘中读取到之后 加入内存缓存
    adddrawabletomemorycache(key, drawable);
    return drawable;
   }
  } catch (ioexception e) {
   e.printstacktrace();
  }
  return null;
 }

 /**
  * 添加bitmap到磁盘缓存
  *
  * @param key
  * @param value
  */
 private void addbitmaptodiskcache(string key, byte[] value) {
  outputstream out = null;
  try {
   disklrucache.editor editor = mdiskcache.edit(key);
   if (editor != null) {
    out = editor.newoutputstream(0);
    if (value != null && value.length > 0) {
     out.write(value);
     out.flush();
     editor.commit();
    } else {
     editor.abort();
    }
   }
   mdiskcache.flush();
  } catch (ioexception e) {
   e.printstacktrace();
  } finally {
   disklruutils.closequietly(out);
  }
 }

 private drawable download(string imageurl) {
  httpurlconnection urlconnection = null;
  bytearrayoutputstream bos = null;
  inputstream ins = null;
  try {
   final url url = new url(imageurl);
   urlconnection = (httpurlconnection) url.openconnection();
   ins = urlconnection.getinputstream();
   bos = new bytearrayoutputstream();
   int b;
   while ((b = ins.read()) != -1) {
    bos.write(b);
   }
   bos.flush();
   byte[] bytes = bos.tobytearray();
   bitmap bitmap = disklruutils.bytes2bitmap(bytes);
   string key = disklruutils.hashkeyfordisk(imageurl);
   drawable drawable = disklruutils.bitmap2drawable(bitmap);
   //加入内存缓存
   // adddrawabletomemorycache(key, drawable);
   //加入磁盘缓存
   addbitmaptodiskcache(key, bytes);
   return drawable;
  } catch (ioexception e) {
   e.printstacktrace();
  } finally {
   if (urlconnection != null) {
    urlconnection.disconnect();
   }
   disklruutils.closequietly(bos);
   disklruutils.closequietly(ins);
  }
  return null;
 }

 /**
  * 从缓存中移除
  *
  * @param key
  */
 public void removecache(string key) {
  removecachefrommemory(key);
  removecachefromdisk(key);
 }

 /**
  * 从内存缓存中移除
  *
  * @param key
  */
 public void removecachefrommemory(string key) {
  mmemorycache.remove(key);
 }

 /**
  * 从磁盘缓存中移除
  *
  * @param key
  */
 public void removecachefromdisk(string key) {
  try {
   mdiskcache.remove(key);
  } catch (exception e) {
  }
 }

 /**
  * 磁盘缓存大小
  *
  * @return
  */
 public long diskcachesize() {

  return mdiskcache.size();
 }

 /**
  * 内存缓存大小
  *
  * @return
  */
 public long memorycachesize() {

  return mmemorycache.size();
 }

 /**
  * 关闭磁盘缓存
  */
 public void closediskcache() {
  try {
   mdiskcache.close();
  } catch (exception e) {
  }
 }

 /**
  * 清理缓存
  */
 public void cleancache() {
  cleanmemoryccache();
  cleandiskcache();
 }

 /**
  * 清理磁盘缓存
  */
 public void cleandiskcache() {
  try {
   mdiskcache.delete();
  } catch (exception e) {
  }
 }

 /**
  * 清理内存缓存
  */
 public void cleanmemoryccache() {
  mmemorycache.evictall();
 }
}

disklruutils.java

final class disklruutils {

 /**
  * 关闭输入输出流
  */
 public static void closequietly(/*auto*/closeable closeable) {
  if (closeable != null) {
   try {
    closeable.close();
   } catch (runtimeexception rethrown) {
    throw rethrown;
   } catch (exception ignored) {
   }
  }
 }

 /**
  * 获取versioncode
  */
 public static int getappversion(context context) {
  try {
   packageinfo info = context.getpackagemanager().getpackageinfo(context.getpackagename(), 0);
   return info.versioncode;
  } catch (packagemanager.namenotfoundexception e) {
   e.printstacktrace();
  }
  return 1;
 }


 public static string hashkeyfordisk(string key) {
  string cachekey;
  try {
   final messagedigest mdigest = messagedigest.getinstance("md5");
   mdigest.update(key.getbytes());
   cachekey = bytestohexstring(mdigest.digest());
  } catch (nosuchalgorithmexception e) {
   cachekey = string.valueof(key.hashcode());
  }
  return cachekey;
 }

 public static string bytestohexstring(byte[] bytes) {
  stringbuilder sb = new stringbuilder();
  for (int i = 0; i < bytes.length; i++) {
   string hex = integer.tohexstring(0xff & bytes[i]);
   if (hex.length() == 1) {
    sb.append('0');
   }
   sb.append(hex);
  }
  return sb.tostring();
 }

 /**
  * bitmap → bytes
  */
 public static byte[] bitmap2bytes(bitmap bm) {
  if (bm == null) {
   return null;
  }
  bytearrayoutputstream baos = new bytearrayoutputstream();
  bm.compress(bitmap.compressformat.png, 100, baos);
  return baos.tobytearray();
 }

 /**
  * bytes → bitmap
  */
 public static bitmap bytes2bitmap(byte[] bytes) {
  return bitmapfactory.decodebytearray(bytes, 0, bytes.length);
 }


 /**
  * drawable → bitmap
  */
 public static bitmap drawable2bitmap(drawable drawable) {
  if (drawable == null) {
   return null;
  }
  // 取 drawable 的长宽
  int w = drawable.getintrinsicwidth();
  int h = drawable.getintrinsicheight();
  // 取 drawable 的颜色格式
  bitmap.config config = drawable.getopacity() != pixelformat.opaque ? bitmap.config.argb_8888 : bitmap.config.rgb_565;
  // 建立对应 bitmap
  bitmap bitmap = bitmap.createbitmap(w, h, config);
  // 建立对应 bitmap 的画布
  canvas canvas = new canvas(bitmap);
  drawable.setbounds(0, 0, w, h);
  // 把 drawable 内容画到画布中
  drawable.draw(canvas);
  return bitmap;
 }

 /*
   * bitmap → drawable
   */
 public static drawable bitmap2drawable(bitmap bm) {
  if (bm == null) {
   return null;
  }
  bitmapdrawable bd = new bitmapdrawable(bm);
  bd.settargetdensity(bm.getdensity());
  return new bitmapdrawable(bm);
 }

}

 以上就是基于lru图片缓存简单实现,希望对大家的学习有所帮助,也希望大家多多支持。