Glide源码分析

常见用法:

进阶点的:

Glide加载流程

Glide的加载过程大致如下,Glide#with获取与生命周期绑定的RequestManager。

RequestManager通过load获取对应的RequestBuilder。根据RequestBuilder构建对应的Request, 然后将Request交给RequestManager进行统一管理,同时确定加载的model类型。

之后通过into方法,将传入的view在glide底层被封装成了一个target对象,target能够获取自身绑定的请求;

然后开始调用RequestManager#track开始进行图片请求。request通过Engine分别尝试从弱引用缓存、Lru缓存、文件缓存中加载图片,当以上的缓存中都不存在对应的图片后,会从网络中获取。网络获取的数据最后进行model类型匹配、解码、图片变换、转换。

Glide缓存:

简单介绍:Glide的缓存机制,主要分为3种缓存,一种是内存缓存(分为Lru算法的内存缓存和弱引用缓存),一种是磁盘缓存。

详细介绍:
读取一张图片的时候,获取顺序:弱引用缓存(loadfromActiveResources)-》Lru算法缓存(loadFromMemory())-》磁盘缓存(fun: loadFromCache() 如果设置了的话)

当我们的APP中想要加载某张图片时,先去弱引用缓存(activeResources)中找图片,如果引用中有资源直接取出来用,如果没有则尝试取内存缓存(memoryCache)中找。如果内存缓存中找到图片则取出来用并将缓存存到弱引用缓存中,没有则去磁盘缓存(diskCache)中找,如果找到则取出来用并把缓存分别存到内存缓存和弱引用缓存中,没有则再去请求网络;如果网络加载数据成功,则将成功数据分别缓存到磁盘、内存、弱引用三级缓存中;

将图片缓存的时候,写入顺序:磁盘缓存-》Lru算法缓存-》弱引用缓存 中。

弱引用缓存

glide会采用一个acquired(int)变量用来记录图片被引用的次数, 当acquired变量大于0的时候,说明图片正在使用中,也就是将图片放到弱引用缓存当中;如果acquired变量等于0了,说明图片已经不再被使用了,那么此时会调用方法来释放资源,会将缓存图片从弱引用中移除,这样也就实现了正在使用中的图片使用弱引用来进行缓存。

进阶不确定内容,不主动记(图片资源引用计数被清零的时候就会从弱引用缓存中移除,加入lru cache中。而如果这个时候lru cache满了的话就会对最近最久未使用的图片资源丢到复用池Bitmap复用池);

LruCache内存缓存:

最近最少使用算法,设定一个缓存大小,当缓存达到这个大小之后,会将最老的数据移除,避免图片占用内存过大导致OOM。LruCache 内部用LinkHashMap存取数据,在双向链表保证数据新旧顺序的前提下,设置一个最大内存,往里面put数据的时候,当数据达到最大内存的时候,将最老的数据移除掉,保证内存不超过设定的最大值。


关于LinkedHashMap:

LinkHashMap继承HashMap,在 HashMap的基础上,新增了双向链表结构,每次访问数据的时候,会更新被访问的数据的链表指针,具体就是先在链表中删除该节点,然后添加到链表头header之前,这样就保证了链表头header节点之前的数据都是最近访问的(从链表中删除并不是真的删除数据,只是移动链表指针,数据本身在map中的位置是不变的)。

磁盘缓存:

四种缓存方式:

  1. DiskCacheStrategy.NONE 不缓存文件
  2. DiskCacheStrategy.SOURCE 只缓存原图
  3. DiskCacheStrategy.RESULT 只缓存最终加载的图(默认的缓存略)
  4. DiskCacheStrategy.ALL 同时缓存原图和结果图

Glide生命周期管理

Glide通过给Fragment/Activity插入一个不可见的Fragment,通过监听该Fragment的生命周期。来实现对应的请求管理。但是需要注意的是,如果在Fragment中使用Activity,在图片的请求过程中Fragment被销毁,但是请求并没有结束,会造成内存泄漏。

         但是如果,是在非生命周期的组件上进行时,会采用Application 的生命周期贯穿整个应用,所以 applicationManager 只有在应用程序关闭的时候终止加载。当上下文对象传入的是非全局context,如果glide运行在非UI线程,使用全局上下文,那么glide加载图片跟随application的生命周期。如果glide在某个activity加载图片,生命周期和activity同步。

Glide RequestOptions请求参数配置

可以看到apply对RequestOption设置时,可以通过new不同的类去做请求参数配置,总共分了三级配置: 

第一级作用于Glide ,在RequestManger的构造方法进行配置;

第二级作用于RequestOption ,是针对某个RequestOption进行配置处理。

第三级作用于RequestBuilder,通过它的Apply方法为每一个请求的配置进行更新;

图片加载回调

Glide 通过into加载的图片会通过Executors#MAIN_THREAD_EXECUTOR回调到主线程。

Glide中的线程池配置

1.DiskCacheExecutor 该线程池只有一个核心线程,没有非核心线程,所有任务在线程池中串行执行。在Glide中常用与从文件中加载图片。

2.SourceExecutor 该线程也只有核心线程没有非核心线程,与DiskCacheExecutor 的不同之处在于核心线程的数量根据CPU的核数来决定。如果cpu核心数超过4则核心线程数为4 如果Cpu核心数小于4那么使用Cpu核心数作为核心线程数量。在Glide中长用来从网络中加载图片。

3.UnlimitedSourceExecutor 没有核心线程,非核心线程数量无限大。这种类型的线程池常用于执行量大而快速结束的任务。在所有任务结束。在所有任务结束后几乎不消耗资源。

4.AnimationExecutor 没有核心线程,非核心线程数量根据Cpu核心数来决定,当Cpu核心数大于等4时 非核心线程数为2,否则为1。

Q0:假如让你自己写个图片加载框架,你会考虑哪些问题?

  • 异步加载:线程池
  • 切换线程:Handler,没有争议吧
  • 缓存:LruCache、DiskLruCache
  • 防止OOM:软引用、LruCache、图片压缩、Bitmap像素存储位置
  • 内存泄露:注意ImageView的正确引用,生命周期管理
  • 列表滑动加载的问题:加载错乱、队满任务过多问题

详细计划:

1.由于采用三级缓存策略:内存缓存,磁盘,网络; 所以如下分析要三个线程池差不多; 

2.切换线程刷新UI;

3.内存缓存:LruCache;磁盘缓存;网络(上面分析过就不展开分析了)

4.防止OOM:LruCache会移除最近一直没用的对象保证不会OOM,但如果图片单张过大,可以考虑降低图片bitmap格式,不采用argb8888而是采用rgb565;

5.listview列表异步加载图片错乱问题:给view设置tag;

Q1:为什么选择Glide库:

  • 支持多样化媒体加载
    Glide 不仅是一个图片缓存,它支持 Gif、WebP、缩略图,甚至是 Video
  • 支持多种图片格式(Gif、WebP、Video), 扩展灵活
  • 使用方便,API简洁。with、load、into 三步就可以加载图片
  • 生命周期自动绑定,根据绑定的Activity或Fragment生命周期管理图片请求
  • 高效的缓存策略
    1.支持Memory和Disk图片缓存
    2.一般三方库只会缓存原始尺寸的图片,而 Glide 缓存的是多种规格,即 Glide 会根据你 ImageView 的大小来缓存相应大小的图片尺寸
    3.内存开销小,Glide 默认的 Bitmap 格式是 RGB_565 格式,1像素占用 2 byte,而 Picasso 默认的是 ARGB_8888 格式,1像素占用 4 byte,占用内存要小一半,OOM概率会小一些;

Q2:Glide加载一个一兆的图片(100*100),是否会压缩后再加载,放到一个200*200的view上会怎样,1000*1000呢,图片会很模糊,怎么处理?

当我们调整imageview的大小时,Glide会为每个不同尺寸的Imageview缓存一张图片,也就是说不管你的这张图片有没有加载过,只要imageview的尺寸不一样,那么Glide就会重新加载一次,这时候,它会在加载的imageview之前从网络上重新下载,然后再缓存。举个例子,如果一个页面的imageview是300 * 300像素,而另一个页面中的imageview是100 * 100像素,这时候想要让两个imageview像是同一张图片,那么Glide需要下载两次图片,并且缓存两张图片。

而且其中采用了bitmap复用池:

如果缓存都不存在,那么会从源地址获得图片(网络/文件);如果bitmap缓存池有缓存就直接复用。Bitmap复用可解决的是减少频繁申请内存带来的性能(抖动、碎片)问题BitmapPool是Glide中的Bitmap复用池,同样适用LRU来进行管理。在每次解析一张图片为Bitmap的时候会从其BitmapPool中查找一个可被复用的Bitmap。当一个Bitmap从内存缓存移除时会加入这个BitmapPool

BitmapPoor内部维护了一个Set存储的bitmap,根据bm的大小色彩值等参数作为key存入;

当系统内存不够,主动触发trimMemory的时候,根据策略对Bitmap进行缩减或者清空;

Q3:如何解决数据错乱问题

错乱原因: 因为Adapter对View的复用以及图片网络请求的耗时问题有时候在快速滑动且网络不佳的情况下会出现图片加载错乱。滑动到屏幕内发出网络请求,但并未请求完成就已滑出屏幕,待到请求完成时,就加载到了其它ItemView

方案: setTag

但是在异步加载图片场景:

   String title = dataBean.getTitle();
        String uri = dataBean.getThumbnail_pic_s();
        holder.mImgContentItem.setTag(uri);
        if(holder.mImgContentItem.getTag()!=null&&uri==holder.mImgContentItem.getTag(){
            holder.mImgContentItem.setImageResource();
            holder.mTextContentItem.setText(title);
        }

直接调用 setTag(@Nullable Object tag) 会报错:You must not call setTag() on a view Glide is targeting。

 因为在用into()方法时,就将request作为tag了;

因此采用  setTag(int key, final Object tag)自己传入一个不重复的key即可复用;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值