Android小知识-Android中的缓存策略(内存缓存和磁盘缓存)

傷城~ 2022-03-16 14:12 996阅读 0赞

本篇文章已授权微信公众号 顾林海 独家发布

通过缓存策略,我们不需要每次都从网上请求图片或从存储设备中加载图片,这样就极大地提高了图片的加载效率以及用户体验。目前比较常用的缓存策略是LruCache和DiskLruCache,其中LruCache被称为内存缓存,DiskLruCache被称为存储缓存。Lru是Least Recently Used的缩写,即最近最少使用算法,算法核心思想是:当缓存快满时,会淘汰近期最少使用的缓存目标。

缓存路径:内存->存储设备->网络

LruCache

LruCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,其提供了get和put方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早的缓存对象,然后再添加新的缓存对象。

LruCache是线程安全的。

  • 强引用、软引用和弱引用的区别:
  • 强引用:直接的对象引用。
  • 软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被GC回收。
  • 弱引用:当一个对象只有弱引用存在时,此对象随时被GC回收。

LruCache初始化代码:

  1. private LruCache<String,Bitmap> mMemoryCache;
  2. private void lruCache(){
  3. //当进程可用的最大内存(KB)
  4. int maxMemory= (int) (Runtime.getRuntime().maxMemory()/1024);
  5. //LruCache缓存的总容量
  6. int cacheSize=maxMemory/8;
  7. mMemoryCache=new LruCache<String, Bitmap>(cacheSize){
  8. @Override
  9. protected int sizeOf(String key, Bitmap value) {
  10. return value.getRowBytes()*value.getHeight()/1024;
  11. }
  12. };
  13. }
  14. 复制代码

重写sizeOf方法,用来计算缓存对象的大小。

在一些特殊情况下,还需要重写LruCache的entryRemoved方法,LruCache移除旧缓存时会调用entryRemoved方法,可以在entryRemoved方法中做一些资源回收工作。

DiskLruCache

DiskLruCache用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存的效果。

引入: compile ‘com.jakewharton:disklrucache:2.0.2’

DiskLruCache不能通过构造方法来创建,它提供了open方法用于创建自身,如下代码:

  1. public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) 复制代码

open方法有四个参数,第一个参数表示磁盘缓存在文件系统中的存储路径。缓存路径可以选中SD卡上的缓存目录,具体是指/sdcard/Android/data/package_name/cache 目录,当应用被卸载后,此目录会一并删除,也可以存储在其他指定目录。

第二个参数表示应用的版本号,一般设为1即可。当版本号发生改变时DiskLruCache会清空之前所有的缓存文件。

第三个参数表示单个节点所对应的数据的个数,一般设为1即可。

第四个参数表示缓存的总大小。

DiskLruCache的创建:

  1. private DiskLruCache mDiskLruCache;
  2. private static final long DISK_CACHE_SIZE=1024*1024*50;
  3. private void diskLruCache(){
  4. File diskCacheDir=new File(getCacheDir().getPath()+"/bitmap");
  5. if(!diskCacheDir.exists()){
  6. diskCacheDir.mkdirs();
  7. }
  8. try {
  9. mDiskLruCache=DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);
  10. } catch (IOException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. 复制代码

DiskLruCache的缓存添加的操作是通过Editor完成的,Editor表示一个缓存对象的编辑对象。

DiskLruCache的缓存添加:

  1. private void diskCache(String url){
  2. String key=hashKeyFormUrl(url);
  3. try {
  4. DiskLruCache.Editor editor=mDiskLruCache.edit(key);
  5. if(editor!=null){
  6. OutputStream outputStream=editor.newOutputStream(0);
  7. if(downloadUrlToStream(url,outputStream)){
  8. editor.commit();
  9. }else{
  10. editor.abort();
  11. }
  12. }
  13. } catch (IOException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. private String hashKeyFormUrl(String url){
  18. String cacheKey;
  19. try {
  20. final MessageDigest messageDigest=MessageDigest.getInstance("MD5");
  21. messageDigest.update(url.getBytes());
  22. cacheKey=bytesToHexString(messageDigest.digest());
  23. } catch (NoSuchAlgorithmException e) {
  24. cacheKey=String.valueOf(url.hashCode());
  25. }
  26. return cacheKey;
  27. }
  28. private String bytesToHexString(byte[] bytes){
  29. StringBuilder stringBuilder=new StringBuilder();
  30. for(int i=0,length=bytes.length;i<length;i++){
  31. String hex=Integer.toHexString(0xFF&bytes[i]);
  32. if(hex.length()==1){
  33. stringBuilder.append('0');
  34. }
  35. stringBuilder.append(hex);
  36. }
  37. return stringBuilder.toString();
  38. }
  39. public boolean downloadUrlToStream(String url, OutputStream outputStream){
  40. //进行网络请求,并将数据写入outputStream
  41. return true;
  42. }
  43. 复制代码

DiskLruCache的缓存查找

缓存查找过程也需要将url转换为key,然后通过DiskLruCache的get方法得到一个Snapshot对象,接着再通过Snapshot对象即可得到缓存的文件输入流,有了输入流就可以得到Bitmap对象。

  1. private void getDiskCache(String url){
  2. Bitmap bitmap=null;
  3. String key=hashKeyFormUrl(url);
  4. try {
  5. DiskLruCache.Snapshot snapshot=mDiskLruCache.get(key);
  6. if(snapshot!=null){
  7. FileInputStream fileInputStream= (FileInputStream) snapshot.getInputStream(0);
  8. FileDescriptor fileDescriptor=fileInputStream.getFD();
  9. bitmap=decodeSampleBitmapFromFileDescriptor(fileDescriptor,500,500);
  10. if(bitmap!=null){
  11. //显示图片或是将bitmap进行内存缓存
  12. }
  13. }
  14. } catch (IOException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. public static Bitmap decodeSampleBitmapFromFileDescriptor(FileDescriptor fileDescriptor,int reqWidth,int reqHeight){
  19. final BitmapFactory.Options options=new BitmapFactory.Options();
  20. options.inJustDecodeBounds=true;
  21. BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
  22. options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);
  23. options.inJustDecodeBounds=false;
  24. return BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
  25. }
  26. public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
  27. int outWidth=options.outWidth;
  28. int outHeight=options.outHeight;
  29. int inSampleSize=1;
  30. if(outHeight>reqHeight||outWidth>reqWidth){
  31. final int halfHeight=outHeight/2;
  32. final int halfWidth=outWidth/2;
  33. while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
  34. inSampleSize*=2;
  35. }
  36. }
  37. return inSampleSize;
  38. }
  39. 复制代码

关于LruCache和DiskLruCache的内部实现会在后续文章中给出 (ps:希望大家能关注下我的公众号,毕竟写作不易,可以通过公众号和我交流一些技术,还有大家对Android有哪方面想知道的可以在底下评论,我会选些支持率高的内容进行讲解。)


发表评论

表情:
评论列表 (有 0 条评论,996人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android 缓存机制

    一、Android缓存机制 Android缓存分为内存缓存和文件缓存(磁盘缓存)。在早期,各大图片缓存框架流行之前,常用的内存缓存方式是软引用(SoftReference

    相关 Android Glide缓存策略

    一、glide缓存策略 缓存在请求网络图片时能减少不必要的流量浪费。Glide 缓存分为内存缓存和硬盘缓存,这两个缓存模块的作用各不相同,内存缓存的主要作用是 防止应用重