1. Android Glide4.0+图片加载进度监听
在近期使用Glide4.0+版本的时候,需要进行图片加载进度的监听,于是查找各种资料实现该功能,便有了这篇记录。
笔者Glide为:
大致思路:通过Okhttp的拦截器,监听图片Url的加载进度(需要自己实现逻辑计算),并回调!
1,步骤1,将 OkHttpUrlLoader 添加到项目:
2,步骤2,将 OkHttpStreamFetcher 添加到项目:
3,步骤3,自定义拦截器和回调接口:
4,步骤4,计算加载进度,并在自定义的拦截器中使用:
5,在Glide中启用:
本文仅为记录,详细分析参考: 郭霖大神Glide系列文章
2. Android 【手撕Glide】--Glide缓存机制(面试)
本文源码解析基于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,这样问我这样答
3. Android之 glide 框架 解读
glide图片加载框架 其目的帮助我们在开发过程中对控件上的图片加载减压,它能够通过参数和方法加载 本地图片 网络图片 二进制等到控件上 还能够设置预加载以及加载出错时的错误提示图片,还能够对加载的图片进行缓存重复利用 可以缓存原始照片还能缓存经过压缩处理后的照片,能够缓存到内存,或者硬盘,甚至glide可以加载GIF动图还可以给加载的静态图片指定大小,对于 glide的缓存机制它用的是lru 这种缓存机制 其实就是 在一定缓存空间内把最近用的以及重复用的排列在最高级 将最不常用没有重复使用的排在最低级 当有新实例出现,缓存空间不够用的情况下就会把被打有最低级标志的实例释放掉。
4. Android:深入剖析图片加载库Glide缓存功能(源码分析)
Glide 需要缓存的 图片资源 分为两类:
Glide 的缓存机制使得 Glide 具备非常好的图片缓存效果,从而使得具备较高的图片加载效率。
下面,我将根据 Glide 缓存流程中的每个步骤 进行源码分析。
至此, Glide 的图片缓存 Key 生成完毕。
至此,创建好了缓存对象 LruResourceCache
即:
源码分析如下:
若上述两个方法都没获取到缓存图片时(即内存缓存里没有该图片的缓存),就开启新线程加载图片。
若无法从 内存缓存 里 获得缓存的图片, Glide 就会采用第2级缓存:磁盘缓存 去获取缓存图片
写入 内存缓存分为:写入 弱引用缓存 & LruCache 算法的缓存
写入 LruCache 算法 内存缓存的原理:包含图片资源 resource 的 EngineResource 对象的一个引用机制:
所以:
至此,实现了:
至此, Glide 的图片缓存流程解析完毕。
Android图片加载的那些事:为什么你的Glide 缓存没有起作用?
不定期分享关于 安卓开发 的干货,追求 短、平、快 ,但 却不缺深度 。
5. Android知识点——Glide获取图片宽高
先很负责任的说一下,这个内容也是网络来的,但是很不负责任的是,当初只记录解决方案,忘了记录是查看的哪篇博客了,这里先对不知道借鉴的谁表示感谢。无法分享链接,就厚着脸皮把(转)字去掉了,请大家谅解。
先提供一下 Android知识点——目录 的链接,然后让我们进入正题。
实际上,这篇博客所说的内容并不是所有人都可以用到,毕竟大多数时候,我们只需要展示图片,而并不需要知道图片的宽高;有的时候我们只需要知道展示的宽高(即ImageView)的宽高,不需要知道图片资源的实际尺寸。
但是需求千千万万嘛,以程序员的脑洞,怎么能想到产品的脑洞究竟有多大呢?我这里就遇到了一个需求,那就是需要在一个可缩放的图片上标注icon(类似地图上的marker)。这还不算完,毕竟在找到的图片缩放控件 PhotoView 中,我们点击到图片上后,是有点击点位在整个图片上的百分比坐标回调的。而多端通过百分比是很容易就能在图片中获取到相同的点位,并回显出对应的icon的(没办法,谁让我找的是方便计算百分比的呢),结果Web端优先做了这部分功能,使用的是在原图上的具体坐标。这样我百分比的计划自然就落空了,只能想办法计算出具体的点位。
因此获取图片的原始尺寸就是一个必不可少的环节,我刚刚网络了一下,查到 wangke_king 的 Android获取图片的宽度和高度 中使用的方法是:
我这里没有亲测过,不过应该是没有问题,但是很遗憾我们的需求是在网络图片上做测量,所以这个方法也无法使用,不过如果其他有类似本地图片需求的,不妨尝试一下。而我之前找到的解决方案为:
首先说明,上述的方法是可以实现图片的尺寸测量的,只是有一个小小的问题,那就是想要计算出Drawable的宽高,需要必须等到图片加载完成之后,尝试了使用view.post(),监听组件加载完成,但是并不是每次都能获取到Drawable的宽高,因此当初的解决方案是写了个两秒钟的定时器,每50毫秒测量一次,直到获取到值为止。这样的解决方案可谓是相当无脑了,而且还要消耗很多不必要的资源。
还好皇天不负有心人啊,终于找到了通过Glide获取图片宽高的方式:
这样我们就可以通过回调,在Glide将网络图片注入到对应的图片组件的时候,得到图片的Bitmap,然后在通过Bitmap来获取图片的宽高。但是需要注意的一点是,Bitmap的泛型是需要手动去设置的哦。
另外SimpleTarget现在已经过时,暂时还没有查到。我搜索过SimpleTarget过时使用什么替换,有一些说法是使用BitmapImageViewTarget ,不过下面是实际测试结果。
图片链接:
网络图片-景色
测量结果:
图片信息:
如果不是我使用有误的话,BitmapImageViewTarget 是无法替换SimpleTarget ,实现测量图片原始宽高的功能的。
6. Android Glide(二)生命周期感知
主要分为三个层次的生命周期:Activity & 网络 & 内存。
我们一般认为,应该及时取消不必要的加载请求,这很耗费资源,但在Glide这并不是必须的操作。因为 Glide 会在页面生命周期 / 网络变化时,自动取消加载或重新加载。没错,就是这么牛逼。
还记得Glide使用的第一句吗,Glide.with(参数),
with()方法可以接收Context、Activity或者Fragment类型的参数。
先说结论,根据传入的参数不同,将对应于 Application & Activity & Fragment 的作用域,具体如下:
这里不细节分析源码实现,我们只需要知道,不同参数通过重载方法,作用于不同的作用域
这里具体分析一下上面这些晦涩的语句,注意设计的思路重点,就是这个SupportRequestManagerFragment,这个隐藏SupportRequestManagerFragment,这个并不可见的SupportRequestManagerFragment。
Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,相当于通过这个隐藏的fragment与对应的Activity建立了联系,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。
这是 为了避免 SupportRequestManagerFragment 在一个作用域中重复创建。
因为commitAllowingStateLoss()是将事务 post 到消息队列中的,也就是说,事务是异步处理的,而不是同步处理的。假设没有临时保存记录,一旦在事务异步等待执行时调用了Glide.with(...),就会在该作用域中重复创建 Fragment。
从上面的分析我们得知,Glide 为每个Activity 和 Fragment 作用域创建了一个无界面的 Fragment,这里我们就来分析 Glide 如何监听这个无界面 Fragment 的生命周期。
首先先提到一个概念,Lifecycle,大家可以粗略的把它理解为 移动APP页面的状态
其实说起来也是三步型宴陪走战略,
1、在创建Fragment的时候会创建ActivityFragmentLifecycle对象;
2、在Fragment生命周期的方法中会调用Lifecycle的相关方法来通知RequestManager;
3、LifecycleListener 是一个接口,RequestManager实现了这个接口,Lifecycle最终是调用了lifecycleListener来通知相关的实现类的,也就是RequestManager。
这个就非常简单了,也就是上述的RequestManager实现了LifecycleListener接口后,在对应的方法中,作出相应的处理。
主要关注以下几点:
这个逻辑很简单,在刚才所说的RequestManager的构造器中,会构建一个ConnectivityMonitor对象,它的默认构造工厂是,如果有网络监听权限,
则实例化DefaultConnectivityMonitor,在onStart()时注册广播监卜蠢听器,而在onStop()时注销广播监听器。在RequestManager中根据网络状态进行相应的操作。
听起祥睁来有点拗口,简而言之,如果应用有监控网络状态的权限,那么 Glide 会监听网络连接状态,在页面可见时注册广播监听器,而在页面不可见时注销广播监听器,并在网络重新连接时重新启动失败的请求。
这个的实现也非常简单的的,在构建 Glide 时,会调用registerComponentCallbacks()进行全局注册, 系统在内存紧张的时候回调onTrimMemory(level)。
而 Glide 则根据系统内存紧张级别(level)进行相应的回收,而 RequestManager 在 TRIM_MEMORY_MODERATE 级别会暂停请求。