Glide实现原理解析
一.Glide缓存机制
Glide采取的多级缓存机制,能够较为友好地实现图片、动图的加载。其主要有 内存缓存+磁盘缓存 ,当然他们的作用也有不同,其中内存缓存主要用于防止将重复的图读入内存中,磁盘缓存则用于防止从网络或者其他地方将重复下载和数据读取。
默认情况下,Glide 会在开始一个新的图片请求之前检查以下多级的缓存:
1.活动资源 (Active Resources)
2. 内存缓存 (Memory Cache)
3.资源类型(Resource Disk Cache)
4.原始数据 (Data Disk Cache)
活动资源:如果当前对应的图片资源正在使用,则这个图片会被Glide放入活动缓存。
内存缓存:如果图片最近被加载过,并且当前没有使用这个图片,则会被放入内存中
资源类型: 被解码后的图片写入磁盘文件中,解码的过程可能修改了图片的参数(如:inSampleSize。inPreferredConfig)
原始数据: 图片原始数据在磁盘中的缓存(从网络、文件中直接获得的原始数据)
在调用into之后,Glide会首先从Active Resources查找当前是否有对应的活跃图片,没有则查找内存缓存,没有则查找资源类型,没有则查找数据来源。
1.活动资源
活动资源中是一个”引用计数"的图片资源的弱引用集合。使用一个 Map
此外还有一个引用队列ReferenceQueue
2.内存缓存
内存缓存默认使用LRU(缓存淘汰算法/最近最少使用算法),当资源从活动资源移除的时候,会加入此缓存。使用图片的时候会主动从此缓存移除,加入活动资源。LRU在Android support-v4中提供了LruCache工具类。
3.磁盘缓存
资源类型缓存的是经过解码后的图片,如果再使用就不需要再去进行解码配置(BitmapFactory.Options),加快获得图片速度。比如原图是一个100x100的ARGB_8888图片,在首次使用的时候需要的是50x50的RGB_565图片,那么Resource将50x50 RGB_565缓存下来,再次使用此图片的时候就可以从 Resource 获得。不需要去计算inSampleSize(缩放因子)。
原始数据缓存的则是图像原始数据。
Glide
Glide.with(this).load(url).into(imageView);
三步走:先with(),再load(),最后into()
with()方法
是Glide类中的一组静态方法,
with()方法的重载种类非常多,既可以传入Activity,也可以传入Fragment或者是Context。每一个with()方法重载的代码都非常简单,都是先调用RequestManagerRetriever的静态get()方法得到一个
RequestManagerRetriever对象,这个静态get()方法就是一个单例实现,
然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象。
load()方法
参数类型:String,File,byte[],URL,图片资源ID,Uri,我这里只看参数类型为String类型的,这些方法都会返回DrawableTypeRequest对象。
image.png
load内部调用了fromString方法,fromString方法调用了loadGeneric方法,
loadGeneric()方法也没几行代码,这里分别调用了Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法来获得ModelLoader对象。
ModelLoader对象是用于加载图片的,而我们给load()方法传入不同类型的参数,这里也会得到不同的ModelLoader对象,最后返回一个DrawableTypeRequest对象。
DrawableTypeRequest的父类是DrawableRequestBuilder,
DrawableRequestBuilder中有很多个方法,这些方法其实就是Glide绝大多数的API了。里面有不少我们在上篇文章中已经用过了,比如说placeholder()方法、error()方法、diskCacheStrategy()方法、override()方法等,都是在DrawableRequestBuilder 类里面。提供了glide加载图片过程的很多方法
into()方法的作用:
①初始化各种参数,做好准备工作(网络请求、基于MVP的各种接口回调),②使用最原始的HTTPConnect网络连接,读取文件流,③根据文件判断是GIF动图还是Bitmap静态图片,④通过相关复杂逻辑将下载的图片资源取出来,赋值给ImageView控件。
Glide的核心思想:
对象池:
Glide原理的核心是为bitmap维护一个对象池。对象池的主要目的是通过减少大对象内存的分配以重用来提高性能。
生命周期绑定:
第一个with方法,这个其实就是一个工厂方法,虽然有许多重载的形式,其实都是要创建一个RequestManager对象。
创建一个透明的 RequestManagerFragment 加入到FragmentManager 之中
通过添加的这个 Fragment 感知 Activity 、Fragment 的生命周期。
图片的加载任务会与activity或者Fragment的生命周期绑定,当界面执行onStop的使用自动暂定,而当执行onStart的时候又会自动重新开启,同样的,动态Gif图的加载也是如此,以用来节省电量,同时Glide会对网络状态做监听,当网络状态发生改变时,会重启失败的任务,以减少任务因网络连接问题而失败的概率。
Bitmap复用池
如果缓存都不存在,那么会从源地址获得图片(网络/文件)。而在解析图片的时候会需要可以获得BitmapPool(复用池),达到复用的效果。复用并不能减少程序正在使用的内存大小。Bitmap复用,解决的是减少频繁申请内存带来的性能(抖动、碎片)问题。
BitmapPool是Glide中的Bitmap复用池,同样适用LRU来进行管理。在每次解析一张图片为Bitmap的时候(磁盘缓存、网络/文件)会从其BitmapPool中查找一个可被复用的Bitmap。当一个Bitmap从内存缓存 被动 的被移除(内存紧张、达到maxSize)的时候并不会被recycle。而是加入这个BitmapPool,只有从这个BitmapPool 被动的被移除的时候,Bitmap的内存才会真正被recycle释放。
二.Glide生命周期管理
Glide在Glide.with(context)中就实现了生命周期管理,with根据传入的参数有不同的实现。
//传入一个Context public static RequestManager with(@NonNull Context context)
//传入一个activity public static RequestManager with(@NonNull Activity activity)
//传入一个FragmentActivity public static RequestManager with(@NonNull FragmentActivity activity)
//传入一个Fragment public static RequestManager with(@NonNull Fragment fragment)
//传入一个View public static RequestManager with(@NonNull View view)
虽然有这么多类型,但其实可以分为两类的。
传入一个ApplicationContext,Glide的生命周期就相当于绑定了整个应用,只要应用不退出,任何时候都能够加载,也可以理解为不对Glide生命周期进行管理。
传入activity、FragmentActivity 、Fragment 及View ,这样就会创建一个看不见的fragment,Glide的生命周期就随着该Fragment的变化而变化。
由于ActivityFragmentLifecycle对象是在fragment中创建并且它的onStart、onStop、onDestory方法与fragment一一对应,这样就将RequestManager的生命周期就与fragment关联起来了,也就与当前activity关联起来。总体流程如下:
当fragment生命周期发生变化时,通过ActivityFragmentLifecycle将变化告诉给RequestManager与DefaultConnectivityMonitor。而RequestManager又将此变化告诉给ImageViewTarget。
至于传入参数为其他类型的实现基本上与activity的类似,就不在叙述。