Android 【手撕Glide】--Glide缓存机制(面试)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 【手撕Glide】--Glide缓存机制(面试)相关的知识,希望对你有一定的参考价值。

参考技术A

本文源码解析基于Glide 4.6.1

系列文章
Android 【手撕Glide】--Glide缓存机制
Android 【手撕Glide】--Glide缓存机制(面试)
Android 【手撕Glide】--Glide是如何关联生命周期的?

Glide缓存分为内存缓存和磁盘缓存,其中内存缓存是由弱引用+LruCache组成。

取的顺序是:弱引用、LruCache、磁盘
存的顺序是:磁盘、弱引用、LruCache

这张亲手制作的图片,方便大家更直观的理解缓存机制的整体流程,结合文末总结效果更佳。喜欢的记得点赞!

概述

1、弱引用是由这样一个HashMap维护,key是缓存的key,这个key由图片url、width、height等10来个参数组成;value是图片资源对象的弱引用形式。

2、LruCache是由一个LinkedHashMap维护,根据Lru算法来管理图片。大致的原理是利用linkHashMap链表的特性,把最近使用过的文件插入到列表头部,没使用的图片放在尾部;然后当图片大小到达预先设置的一个阀值的时候 ,按算法删除列表尾部的部分数据。由于篇幅有限,这里不讲解LruCache和DiskLruCache的底层原理,这里推荐一篇 图解LinkedHashMap原理

这是Glide自定义的LruCache

存取原理
取数据
在内存缓存中有一个概念叫图片引用计数器 ,具体来说是在 EngineResource 中定义一个 acquired 变量用来记录图片被引用的次数,调用 acquire() 方法会让变量加1,调用 release() 方法会让变量减1。

获取图片资源是先从弱引用取缓存,拿到的话,引用计数+1;没有的话从LruCache中拿缓存,拿到的话,引用计数也是+1,同时把图片从LruCache缓存转移到弱应用缓存池中;再没有的话就通过 EngineJob 开启线程池去加载图片,拿到的话,引用计数也是+1,会把图片放到弱引用。

存数据
很明显,这是加载图片之后的事情。通过 EngineJob 开启线程池去加载图片,取到数据之后,会回调到主线程,把图片存到弱引用。当图片不再使用的时候,比如说暂停请求或者加载完毕或者清除资源时,就会将其从弱引用中转移到 LruCache 缓存池中。 总结一下,就是正在使用中的图片使用 弱引用 来进行缓存,暂时不用的图片使用 LruCache 来进行缓存的功能;同一张图片只会出现在 弱引用 和 LruCache 中的一个。

为什么要引入软引用?
1、分压策略,减少Lrucache 中 trimToSize 的概率。如果正在remove的是张大图,lrucache正好处在临界点,此时remove操作,将延缓Lrucache的 trimToSize 操作;
2 提高效率:弱引用用的是 HashMap ,Lrucache用的是 LinkedHashMap ,从访问效率而言,肯定是 HashMap 更高。

Glide磁盘缓存策略(4.x)

如果在内存缓存中没获取到数据会通过 EngineJob 开启线程池去加载图片,这里有2个关键类: DecodeJob 和 EngineJob 。 EngineJob 内部维护了线程池,用来管理资源加载,当资源加载完毕的时候通知回调; DecodeJob 是线程池中的一个任务。

磁盘缓存是通过 DiskLruCache 来管理的,根据缓存策略,会有2种类型的图片, DATA (原始图片)和 RESOURCE (转换后的图片)。磁盘缓存依次通过 ResourcesCacheGenerator 、 SourceGenerator 、 DataCacheGenerator 来获取缓存数据。 ResourcesCacheGenerator 获取的是转换过的缓存数据; SourceGenerator 获取的是未经转换的原始的缓存数据; DataCacheGenerator 是通过网络获取图片数据再按照按照缓存策略的不同去缓存不同的图片到磁盘上。

Glide缓存分为 弱引用+ LruCache+ DiskLruCache ,其中读取数据的顺序是:弱引用 > LruCache > DiskLruCache>网络;写入缓存的顺序是:网络 --> DiskLruCache--> LruCache-->弱引用

内存缓存分为弱引用的和 LruCache ,其中正在使用的图片使用弱引用缓存,暂时不使用的图片用 LruCache缓存,这一点是通过 图片引用计数器(acquired变量)来实现的,详情可以看内存缓存的小结。

磁盘缓存就是通过DiskLruCache实现的,根据缓存策略的不同会获取到不同类型的缓存图片。它的逻辑是:先从转换后的缓存中取;没有的话再从原始的(没有转换过的)缓存中拿数据;再没有的话就从网络加载图片数据,获取到数据之后,再依次缓存到磁盘和弱引用。

参考:
面试官:简历上最好不要写Glide,不是问源码那么简单
原来面试的时候写精通Glide,这样问我这样答

Android中的Glide加载图片

注意:在Android Studio的项目的build.gradle中添加:

 

compile ‘com.github.bumptech.glide:glide:3.6.1‘

 

然后同步一下

 

目录:

  1.     使用Glide结合列表的样式进行图片加载
  2.     如果使用的是RecyclerView,可以在Adapter的onBindViewHolder方法中使用
  3.     当加载网络图片时,由于加载过程中图片未能及时显示,此时可能需要设置等待时的图片,通过placeHolder()方法
  4.     当加载图片失败时,通过error(Drawable drawable)方法设置加载失败后的图片显示
  5.     图片的缩放,centerCrop()和fitCenter()
  6.     显示gif动画
  7.     显示本地视频
  8.     缓存策略
  9.     优先级,设置图片加载的顺序
  10.     当不需要将加载的资源直接放入到ImageView中而是想获取资源的Bitmap对象
  11.     集成网络栈(okHttp,Volley)
 
1.使用Glide结合列表的样式进行图片加载:
        1)    如果使用的是ListView,可以直接在Adapter的getView方法中使用:                
  1. @Override
  2. public View getView(int position, View convertView, ViewGroup parent) {
  3. if (null == convertView) {
  4. //.....
  5. }
  6. Glide
  7. .with(context)
  8. .load(imageUrls[position])
  9. .into(holder.imageView);
  10. return convertView;
  11. }
 
        2)    如果使用的是RecyclerView,可以在Adapter的onBindViewHolder方法中使用:        
  1. @Override
  2. public void onBindViewHolder(RVViewHolder holder, int position) {
  3. Glide.with(MainActivity.this)
  4. .load(args[position])
  5. .into(holder.imageView);
  6. }
       
         3)    当加载网络图片时,由于加载过程中图片未能及时显示,此时可能需要设置等待时的图片,通过placeHolder()方法: 
  1. Glide
  2. .with(context)
  3. .load(UsageExampleListViewAdapter.eatFoodyImages[0])
  4. .placeholder(R.mipmap.ic_launcher) // can also be a drawable
  5. .into(imageViewPlaceholder);
        
        4)    当加载图片失败时,通过error(Drawable drawable)方法设置加载失败后的图片显示:
  1. Glide
  2. .with(context)
  3. .load("http://futurestud.io/non_existing_image.png")
  4. .error(R.mipmap.future_studio_launcher) // will be displayed if the image cannot be loaded
  5. .into(imageViewError);
 
        5)    图片的缩放,centerCrop()和fitCenter():        
  1. //使用centerCrop是利用图片图填充ImageView设置的大小,如果ImageView的
  2. //Height是match_parent则图片就会被拉伸填充
  3. Glide.with(MainActivity.this)
  4. .load(args[position])
  5. .centerCrop()
  6. .into(holder.imageView);
  1. //使用fitCenter即缩放图像让图像都测量出来等于或小于 ImageView 的边界范围
  2. //该图像将会完全显示,但可能不会填满整个 ImageView。
  3. Glide.with(MainActivity.this)
  4. .load(args[position])
  5. .fitCenter()
  6. .into(holder.imageView);
        6)    显示gif动画:
  1. Glide
  2. .with( context )
  3. .load( gifUrl )
  4. .asGif() //判断加载的url资源是否为gif格式的资源
  5. .error( R.drawable.full_cake )
  6. .into( imageViewGif );
 
        7)    显示本地视频
  1. String filePath = "/storage/emulated/0/Pictures/example_video.mp4";
  2. Glide
  3. .with( context )
  4. .load( Uri.fromFile( new File( filePath ) ) )
  5. .into( imageViewGifAsBitmap );
        8)    缓存策略:
  1. Glide
  2. .with( context )
  3. .load( Images[0] )
  4. .skipMemoryCache( true ) //跳过内存缓存
  5. .into( imageViewInternet );
  1. Glide
  2. .with( context )
  3. .load( images[0] )
  4. .diskCacheStrategy( DiskCacheStrategy.NONE ) //跳过硬盘缓存
  5. .into( imageViewInternet );
  • DiskCacheStrategy.NONE 什么都不缓存
  • DiskCacheStrategy.SOURCE 仅仅只缓存原来的全分辨率的图像
  • DiskCacheStrategy.RESULT 仅仅缓存最终的图像,即降低分辨率后的(或者是转换后的)
  • DiskCacheStrategy.ALL 缓存所有版本的图像(默认行为
        9)    优先级,设置图片加载的顺序:
    
  • Priority.LOW
  • Priority.NORMAL
  • Priority.HIGH
  • Priority.IMMEDIATE   
  1. private void loadImageWithHighPriority() {
  2. Glide
  3. .with( context )
  4. .load( mages[0] )
  5. .priority( Priority.HIGH )
  6. .into( imageViewHero );
  7. }
  8. private void loadImagesWithLowPriority() {
  9. Glide
  10. .with( context )
  11. .load( images[1] )
  12. .priority( Priority.LOW )
  13. .into( imageViewLowPrioLeft );
  14. Glide
  15. .with( context )
  16. .load( images[2] )
  17. .priority( Priority.LOW )
  18. .into( imageViewLowPrioRight );
  19. }
 
        10)    当不需要将加载的资源直接放入到ImageView中而是想获取资源的Bitmap对象:
  1. //括号中的300,600代表宽和高但是未有作用
  2. SimpleTarget target = new SimpleTarget<Bitmap>(300,600) {
  3. @Override
  4. public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
  5. holder.imageView.setImageBitmap(resource);
  6. }
  7. };
  8. Glide.with(MainActivity.this)
  9. .load(args[position])
  10. .asBitmap()
  11. .into(target);
        
        11)    集成网络栈(okHttp,Volley):  
  1. dependencies {
  2. // your other dependencies
  3. // ...
  4. // Glide
  5. compile ‘com.github.bumptech.glide:glide:3.6.1‘
  6. // Glide‘s OkHttp Integration
  7. compile ‘com.github.bumptech.glide:okhttp-integration:[email protected]
  8. compile ‘com.squareup.okhttp:okhttp:2.5.0‘
  9. }
  1. dependencies {
  2. // your other dependencies
  3. // ...
  4. // Glide
  5. compile ‘com.github.bumptech.glide:glide:3.6.1‘
  6. // Glide‘s Volley Integration
  7. compile ‘com.github.bumptech.glide:volley-integration:[email protected]
  8. compile ‘com.mcxiaoke.volley:library:1.0.8‘
  9. }


以上是关于Android 【手撕Glide】--Glide缓存机制(面试)的主要内容,如果未能解决你的问题,请参考以下文章

glide4.0以上在Android9.0以上加载图片不显示解决方案

Android Glide图片框架的使用

Android Glide图片框架的使用

Android的报错提示:Failed to resolve: com.github.bumptech.glide:glide:4.9.0

android中glide的缓存时间段

android图片加载库Glide