Glide源码小记

2024-05-30 08:58
文章标签 源码 glide 小记

本文主要是介绍Glide源码小记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Glide 3.7源码阅读要点:

角色介绍:

Glide:对外暴露的统一接口,静态单利,内部成员众多,主要涉及缓存管理和请求管理
Request:抽象的请求接口,定义了一起加载操作相关的生命周期接口
RequestManager:请求管理器,同时是一个生命周期的监听者,其具备网络状态获取,请求管理(委托给RequestTracker实现),生命周期回调的能力
RequestTracker:请求跟踪器,只负责Request的保存和相关生命周期的分发
RequestManagerRetriever: 请求管理器的管理者(请求管理的猎犬),金静态单利,通过上下文信息返回不同的RequestManager
ModelLoader: model加载器,这里的model其实是对应一种输入类型,比如我想加载一个url对应的图片,这个string或者uri对应的类型就是一种model,该接口返回DataFetcher对象,真正的资源获取操作操作都在DataFetcher里
DataFetcher:处理数据获取的操作,比如从网上加载或者从本地目录,一般输出是InputStream
Encoder:数据的写入接口,比如将DataFetcher输出的InputStream写入到某个本地文件(StreamEncoder),一般在数据缓存时会用到这个接口
ResourceDecoder:这个接口对应原始数据类型转换为ViewTarget能使用的操作,比如将InputStream转换为Resource

1. 初始化流程

Glide对象创建
Glide对象的创建是延迟创建,单利对象,采用建造者模式的方式,此外还支持使用方参与其构建过程,我们可以在AndroidManifest文件中的meta-data中配置自定义的GlideModule类来参与。
glide对象
1.内部使用了两个线程池,都是使用的PriorityBlockingQueue阻塞队列和默认的拒绝策略
2.内存缓存也分为了两部分:bitmap缓存和其他内存缓存,其大小比列是4:2(你可以理解成大小分别为4张和2两张截图的大小),这里使用了MemorySizeCalculator这个对象来计算实际大小,该计算器会根据sdk版本和app所申请的实际内存大小来动态计算(实际就是sdk版本和activityManager.isLowRamDevice这个api)
3.diskCacheFactory是一个磁盘缓存工厂类,使用最近最少使用的缓存策略,默认大小是250mb,默认路径为/data/data//cache/image_manager_disk_cache
4.engine就是真正负责处理资源的类,其负责管理资源加载任务,回收资源等工作
Glide构造函数
在Glide的构造函数中,主要关注一下成员:
1.dataLoadProviderRegistry是一个数据加载器的提供者(这里的数据针对的是加载到内存的数据),负责提供不同数据类型数据加载器(不同数据类型之间转码或者数据压缩的工作),比如压缩bitmap等
2.loaderFactory是一个数据加载器工厂,负责提供各种数据加载器(ModelLoader实现者),这里的数据针对的是从磁盘或者在线下载的数据,比如给定一个url,输出一个inputStream。可以看到默认注册了很多的加载器
3.transcoderRegistry中注册了资源转换的转换器,比如将bitmap转换成drawable类型等

  1. 简单流程
Glide.with(applicationContext).load(url).diskCacheStrategy(DiskCacheStrategy.ALL).into(imageView);

上述是我们使用Glide加载一个网络图片的最简单的使用案例,接下来看一下这个最简单的流程源码实现:
1.Glide.with方法由多个重载方法实现,最终都是调用到RequestManagerRetriever的get方法,该方法会根据上下文类型返回不同的RequestManager,这里我使用Application,则会返回包含ApplicationLifecycle的RequestManager实例
RequestManagerRetriever.get
2.RequestManager.load方法也是一个重载的方法,不同的参数类型对应不同的资源加载,我这里是一个String类型的url.这里直接进入fromString方法,最终会转到loadGeneric方法上
RequestManager.load
loadGeneric
loadGeneric方法中主要是获取了相关的modelLoader,最后再返回一个DrawableTypeRequest对象,这里streamModelLoader的实际类型是StreamStringLoader(其真正使用的是HttpUrlGlideUrlLoader类型对象,该对象可以返回HttpUrlFetcher实例,负责做真正的数据获取工作),fileDescriptorModelLoader的实际类型是FileDescriptorStringLoader。
load
返回DrawableTypeRequest之后开始初始化相关的成员参数,load方法的实现如上,这里的ModelType就是我们之前的String类型,model就是url
diskCacheStrategy()
上述方法是缓存模式的设置,glide中支持四种:
缓存模式
DiskCacheStrategy.NONE:表示不缓存
DiskCacheStrategy.SOURCE:表示仅缓存源资源,这里的源资源就是从从网上或者文件中加载到的原始InputStream资源
DiskCacheStrategy.RESULT:表示仅缓存处理(比如资源压缩/尺寸裁剪)之后的资源,不保留原始资源
DiskCacheStrategy.ALL:表示既缓存原始资源,又缓存处理之后的资源
接下来就是into方法的流程了,如图:
GenericRequestBuilder中的into方法
可以看到这里先检验了是否在主线程,标志着该方法只能在主线程调用,之后会检查目标View是否为空,之后会检查该ImageView是否设置了ScaleType,如果设置了,则需要处理图片,我这里都没有设置,最后会调用into的重载方法,传入的参数是GlideDrawableImageViewTarget类型的对象(可以从ImageViewTargetFactory这个工厂类看到具体的类型匹配)
into
上图是into的一个重载方法,传进来的参数target就是GlideDrawableImageViewTarget类型的对象,这里直接看target.getRequest,该方法返回一个Request实例,可以看到其实现就是调用的View.getTag或者View.getTag(tagId);也就是说使用Glide加载图片的View是不能自己调用setTag方法的,
getRequest方法的实现
回到into方法内部,原则上来讲,如果是第一次作为ViewTarget,previous变量为空,如果不是第一次,则会返回之前绑定的request(GenericRequest类型)实例,所以这里先回收了之前的request,之后开始创建本次Request实例并设置相关的成员变量,Request对象的创建是用了对象缓存池,其数据结构是ArrayDeque,之后将request对象和ViewTarget绑定关系,然后将request对象给requestTracker(专职的request生命周期跟踪者),该对象会缓存请求,并调用请求对象的begin方法,开始请求流程
request.begin
begin这个方法先改变request的状态为WAITING_FOR_SIZE,之后检查宽高是否可用,我没有设置过,所以会调用target.getSize来计算宽高,实际上就是获取View的宽高,获取成功之后会调用onSizeReady方法,之后会给targetView设置一个默认加载前的图片
尺寸准备好了
onsizeReady方法先检查并设置了请求的status,之后获取了相关的modelLoader,这里是一个ImageVideoModelLoader实例,之后通过modelLoader获取了一个dataFetcher,这是一个ImageVideoFetcher实例,其中之前的streamLoader(StreamUriLoader类型)和fileDescriptorLoader,实际上最后从网络上加载的操作是委托给了HttpUrlFetcher。之后是获取了资源转换器(GifBitmapWrapperDrawableTranscoder类型),开始进入engine的加载数据环节
engine.load
engine是资源获取的中枢控制器,这里的load方法做了如下事情:
1.针对资源生成了唯一的Key,后续从内存读取缓存资源的key值
2.首先从内存中加载缓存的资源,这里的内存缓存就是我们之前提到的memoryCache(MemoryCache接口的实现者,这里的是LruResourceCache实例),如果命中内存缓存,就调用onRourceReady告诉监听者,这里的监听者就是之前的request对象,这里需要说明一下,当命中缓存后,engine会将命中的资源数据存储到一个hashmap中,表示该资源正在被使用。也就是说glide的内存缓存大小不等于其计算的bitmap池和memoryCache的和
3.如果没有命中缓存,则会尝试从正在使用的hashmap中再去查找一下
4.如果还是没有命中,则先检测是否该资源正在加载的过程中,若是则等他其加载之后的回调,结束本次加载,避免重复
5.如果当前需要的资源没有加载过,则新建EngineJob和相关的DecodeJob,并包装到EngineRunnable中,开始从磁盘或者网络中进行资源加载,从这里开始,后续的操作都在线程池里了,EngineRunnable对象的run方法会直接调用decode方法:
加载数据的入口方法
decode方法首先会判断是否从本地缓存文件中去获取,默认第一次都是先尝试加载本地缓存文件,如果本地缓存文件没有,则会去下载,相关方法如下:
获取本地缓存文件
加载本地缓存的经过处理的数据文件
加载文件
这里的diskCacheProvider是InternalCacheDiskCacheFactory类型的对象,这里首先会获取缓存目录下指定的结果缓存文件(即非原始数据的缓存文件),如果存在则执行decode操作,可以理解为资源类型的转换操作。如果没有获取到非原始的缓存,则会尝试获取原始数据缓存文件;如果获取成功,则本次结束
接着看下从网络获取的流程:
从网络获取
decodeSource方法是入口函数,首先使用fetcher去加载数据,这里实际就是上文提到的HttpUrlFetcher对象,其loadData方法的本质就是使用HttpURLConnection去获取inputStream,decodeSource获取到inputStream之后,调用decodeFromSourceData方法,这个方法会判断是否需要缓存原始数据流,如果需要,则会缓存到本地文件(cacheAndDecodeSourceData方法),之后再从本地将数据加载返回。如果不需要,则直接进行数据的类型转换操作(decode),并返回相关数据给engineJob(调用onResourceReady),然后engineJob发消息给主线程,通知进行ui层面的数据填充。这样一个完整的流程就走完了。

这篇关于Glide源码小记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1016032

相关文章

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

ZOJ Monthly, August 2014小记

最近太忙太忙,只能抽时间写几道简单题。不过我倒是明白要想水平提高不看题解是最好的了。 A  我只能死找规律了,无法证明 int a[50002][2] ;vector< vector<int> > gmax , gmin ;int main(){int n , i , j , k , cmax , cmin ;while(cin>>n){/* g

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m

2014 Multi-University Training Contest 8小记

1002 计算几何 最大的速度才可能拥有无限的面积。 最大的速度的点 求凸包, 凸包上的点( 注意不是端点 ) 才拥有无限的面积 注意 :  凸包上如果有重点则不满足。 另外最大的速度为0也不行的。 int cmp(double x){if(fabs(x) < 1e-8) return 0 ;if(x > 0) return 1 ;return -1 ;}struct poin

2014 Multi-University Training Contest 7小记

1003   数学 , 先暴力再解方程。 在b进制下是个2 , 3 位数的 大概是10000进制以上 。这部分解方程 2-10000 直接暴力 typedef long long LL ;LL n ;int ok(int b){LL m = n ;int c ;while(m){c = m % b ;if(c == 3 || c == 4 || c == 5 ||

2014 Multi-University Training Contest 6小记

1003  贪心 对于111...10....000 这样的序列,  a 为1的个数,b为0的个数,易得当 x= a / (a + b) 时 f最小。 讲串分成若干段  1..10..0   ,  1..10..0 ,  要满足x非递减 。  对于 xi > xi+1  这样的合并 即可。 const int maxn = 100008 ;struct Node{int