目录
- 导读
- 三大主流图片框架对比
- 使用简述
- 源码分析
- with()基本源码深入流程图
- with()方法
- getRetriever() 方法
- get(context)方法
- getAnnotationGeneratedGlideModules方法
- checkAndInitializeGlide()方法
- initializeGlide()方法
- GlideBuilder.build()方法
- load()方法深入了解进程图
- load()方法
- asDrawable().load()方法
- BaseRequestOptions apply()方法
- into()方法深入了解进程图
- into()方法
- glideContext.buildImageViewTarget()
- RequestBuild into()
- RequestBuild buildRequest()
- RequestBuild buildThumbnailRequestRecursive()
- RequestBuild obtainRequest()
- SingleRequest
- RequestManager track()
- RequestManager begin()
- RequestManager onSizeReady()
- Engine load()
- Engine waitForExistingOrStartNewJob()
- Engine start()
- DecodeJob runWrapped()
- SourceGenerator startNext
- DecodeHelper getLoadData()
- ModelLoader getModelLoaders()方法
- ModelLoader buildLoadData()方法
- HttpUrlFetcher loadDataWithRedirects方法
- SourceGenerator onDataReadyInternal方法
- DecodeJob onDataFetcherReady
- DecodeJob decodeFromRetrievedData
- DecodeJob decodeFromData
- LoadPath load()方法
- DecodePath decode()方法
- DecodePath notifyEncodeAndRelease()方法
- EngineJob onResourceReady()方法
- 参考文章
导读
??时隔将近半个月,又到了要学习源码的时候。那么这里我最近就Glide,进行源码解读。至于为什么选择Glide?根本原因还是更常用,因此为了更系统的了解Glide框架,我们开始会就目前三大主流的图片框架进行简要比较,然后根据Glide的三大方法(with,load,into)进行源码解读,在源码的层面上进行分析,那么闲话不多说,直接开始吧!
三大主流图片框架对比
??再说主流框架之前,先提一个老伙计,也就是Universal ImageLoader,这是由白俄罗斯的Sergey Tarasevich开发的一款框架,也是最早用来进行加载图片的主流框架,后来因为容易产生OOM问题(内存溢出,后来好像是提出了图片剪裁的优化方案)且没有针对Background属性开放的api等待因素,在某些企业它已经被淘汰了。
不过它不是我们今天的主角,时代在变化,技术也是,可能今天的Glide在未来某一天也会被代替。不操这个淡心了,我们来看看如今比较主流的三个主角吧:Picasso,Glide和Fresco。作为现在企业项目中常常会用到的对象,我们来看看他们之间的优异,以便你在选择图片框架的时候能择优取之。
Picasso | Fresco | Glide | |
---|---|---|---|
介绍 | Picasso是由Square公司开源的图片缓存库,通过链式编程就可以进行简单使用 | 在图片加载领域,Fresco的确比较专业,是FaceBook退出了一个Android开源图片管理框架,它提供了图片下载、渐进式加载、内存管理等功能。 | Glide是一款谷歌官方推荐使用的强大图片加载框架。它具有使用非常简单、可配置度高、加载的图片格式丰富、加载的图片路径多种、高效的缓存策略和与Activity/Fragment生命周期绑定等特点。 |
优点 | (1) 自带统计监控功能 ??支持图片缓存使用的监控,包括缓存命中率、已使用内存大小、节省的流量等。 (2) 支持优先级处理 ??每次任务调度前会选择优先级高的任务,比如 App 页面中 Banner 的优先级高于 Icon 时就很适用。 (3) 支持延迟到图片尺寸计算完成加载 4) 支持飞行模式、并发线程数根据网络类型而变 ??手机切换到飞行模式或网络类型变换时会自动调整线程池最大并发数,比如 wifi 最大并发为 4, 4g 为 3,3g 为 2。这里 Picasso 根据网络类型来决定最大并发数,而不是 CPU 核数。 (5) “无”本地缓存 ??“无”本地缓存,不是说没有本地缓存,而是 Picasso 自己没有实现,交给了 Square 的另外一个网络库 okhttp 去实现,这样的好处是可以通过请求 Response Header 中的 Cache-Control 及 Expired 控制图片的过期时间。 |
(1) 图片存储在安卓系统的匿名共享内存, 而不是虚拟机的堆内存中, 图片的中间缓冲数据也存放在本地堆内存, 所以, 应用程序有更多的内存使用,不会因为图片加载而导致oom, 同时也减少垃圾回收器频繁调用回收Bitmap导致的界面卡顿,性能更高. (2)渐进式加载JPEG图片, 支持图片从模糊到清晰加载 (3)图片可以以任意的中心点显示在ImageView, 而不仅仅是图片的中心. (4)JPEG图片改变大小也是在native进行的, 不是在虚拟机的堆内存, 同样减少OOM (5)很好的支持GIF图片的显示 |
(1)使用简单 (2)可配置度高,自适应程度高 (3)支持常见图片格式 jpg、png、gif、webp (4)支持多种数据源 网络、本地、资源、Assets 等 (5)高效缓存策略,支持Memory和Disk图片缓存 默认Bitmap格式采用RGB_565内存使用至少减少一半 (6)生命周期集成,根据Activity/Fragment生命周期自动管理请求 (7)高效处理Bitmap 使用Bitmap Pool使Bitmap复用,主动调用recycle回收需要回收的Bitmap,减小系统回收压力 |
缺点 | 不支持GIF, 并且它可能是想让服务器去处理图片的缩放, 它缓存的图片是未缩放的, 并且默认使用ARGB_8888格式缓存图片, 缓存体积大 | Fresco虽然比较专业,如果app是需要进行大量图片处理和优化,或者说是专业处理图片的软件,那么可以考虑使用,但因为框架较大, 影响Apk体积,使用较繁琐所以 | (1)使用方法复杂; (2)由于Glide其功能强大,所以使用的方法非常多,其源码也相对的复杂; (3)包较大; |
那么在日常使用中,我们需求和使用的更多的是使用Glide了,在Glide的使用上,我相信来看这篇文章的各位都没什么问题,如题我们仅仅对Glide的源码进行一步步的剖析,就像我之前在一篇文章上看到,如果面试官问你,Glide怎么使用,你可能信手拈来,但问你怎么去设计这么一款框架时,这就让人有些捉襟见肘了。
图片加载框架可能考虑的需求:
- 异步加载:线程池
- 切换线程:Handler
- 缓存:LruCache、DiskLruCache
- 防止OOM:软引用、LruCache、图片压缩、Bitmap像素存储位置
- 内存泄露:注意ImageView的正确引用,生命周期管理
- 列表滑动加载的问题:加载错乱、队满任务过多问题
- 网络请求策略:图片请求体修改,重定向问题
使用简述
其实Glide的使用方法十分简单,这里是为了让文章分析较为全面进行的一个补充,如果你已经对Glide的使用没什么问题了,可以直接跳过本部分。
和大部分框架一样,这不是安卓内置的,所以需要我们进行依赖添加:
implementation 'com.github.bumptech.glide:glide:4.11.0'annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
如上我用的是4.11的版本,也就是写本文时的一个最新版本,如果你想要查看最新版可以去下面的官网进行查看:
Glide github官网:https://github.com/bumptech/glide
这时候网络权限不要忘了:
<uses-permission android:name="android.permission.INTERNET" />
使用上最简单的方式莫过于with(),load()和into()了
Glide.with(Context context).load(Strint url).into(ImageView imageView);
如果想使用GlideApp进行配置,首先写个自定义继承AppGlideModule的类如下,注解@GlideModule不要忘了。
@GlideModule
public class MGlideApp extends AppGlideModule {@Overridepublic void applyOptions(Context context, GlideBuilder builder) {// 通过builder 参数,可以配置一些Glide的参数,例如缓存大小、MemoryCache、DiskCache、各种Executor 等等,具体可以GlideBuilder的方法}@Overridepublic void registerComponents(@NonNull Context context, @NonNull Glide glide,@NonNull Registry registry) {// 通过参数registry,可以注册 ModelLoader(数据加载) 、Encoder(写入数据)、Decoder()}
}
然后就可以使用自定义的GlideApp了
GlideApp.with(Context context).load(Strint url).into(ImageView imageView);
当然还有一些其他方法,这里简要罗列下:
- placeholder(R.drawable.loading) 加载带有占位图
- error(R.drawable.error 加载失败的图片
- asBitmap()//只加载静态图片,如果是git图片则只加载第一帧
- diskCacheStrategy(DiskCacheStrategy.NONE)//关闭Glide的硬盘缓存机制
//DiskCacheStrategy.NONE: 表示不缓存任何内容。
//DiskCacheStrategy.SOURCE: 表示只缓存原始图片。
//DiskCacheStrategy.RESULT: 表示只缓存转换过后的图片(默认选项)。
//DiskCacheStrategy.ALL : 表示既缓存原始图片,也缓存转换过后的图片。 - asGif()//加载动态图片,若现有图片为非gif图片,则直接加载错误占位图。
- override(100, 100)//指定图片大小
- skipMemoryCache(true) //传入参数为false时,则关闭内存缓存。
使用方法有很多那么这里只是简要罗列其中几项,还有很多方法等着各位去挖掘。
源码分析
因为这篇是我以学习Glide源码为目的,进行步骤化分析,所以整体查看源码的流程也从基本流程开始讲解,当然,后续也会对一些Glide使用方向上进行深层了解,以便我们能更好的使用Glide这个框架。
with()基本源码深入流程图
我们针对with()方法会进行下面图像的深入,以最后返回Glide对象为目的,了解到整个Glide构建的流程和实例实现的过程方法。
with()方法
那么首先就从with()方法开始,了解with()方法的相关过程。
//此方法适用于将在正常片段或活动生命周期之外使用的资源,比如services或notification thumbnails@NonNullpublic static RequestManager with(@NonNull Context context) {return getRetriever(context).get(context);}
//适用于Activity,该加载将与给定的Activity的生命周期绑定@NonNullpublic static RequestManager with(@NonNull Activity activity) {return getRetriever(activity).get(activity);}
//适用于FragmentActivity,该加载将与给定的FragmentActivity的生命周期绑定@NonNullpublic static RequestManager with(@NonNull FragmentActivity activity) {return getRetriever(activity).get(activity);}
//适用于Fragment,该加载将与给定的Fragment的生命周期绑定
//这里的Fragment是androidx.fragment.app.Fragment@NonNullpublic static RequestManager with(@NonNull Fragment fragment) {return getRetriever(fragment.getContext()).get(fragment);}
//适用于Fragment,该加载将与给定的Fragment的生命周期绑定
//这里的Fragment是android.app.Fragment@SuppressWarnings("deprecation")@Deprecated@NonNullpublic static RequestManager with(@NonNull android.app.Fragment fragment) {return getRetriever(fragment.getActivity()).get(fragment);}
//加载与包含视图的Activity或Fragment的生命周期绑定@NonNullpublic static RequestManager with(@NonNull View view) {return getRetriever(view.getContext()).get(view);}
在这里更多是是一个获取操作,利用多态的方式获取你现在的一个状态或者说传入的上下文是什么类型,那么我们继续:
查看getRetriever()方法
getRetriever() 方法
再这个方法中,上下文可能由于其他原因而为空(即,用户传递空值),但实际上,它仅会由于Fragment生命周期错误而发生。如果产生这个错误,那么就需要进行异常抛出,所以这里就进行了为空检测(Fragment生命周期错误导致传值为空)
@NonNullprivate static RequestManagerRetriever getRetriever(@Nullable Context context) {// Context could be null for other reasons (ie the user passes in null), but in practice it will// only occur due to errors with the Fragment lifecycle.//这里利用Preconditions进行参数是否为空的判断,如果为空则抛出带有以下内容的messagePreconditions.checkNotNull(context,"You cannot start a load on a not yet attached View or a Fragment where getActivity() "+ "returns null (which usually occurs when getActivity() is called before the Fragment "+ "is attached or after the Fragment is destroyed).");return Glide.get(context).getRequestManagerRetriever();}
接下来我们继续,查看get(context)方法。
get(context)方法
同上,这也是Glide类中的一个方法,主要是获取glide的单例,如果不为空则返回已存在单例,但如果为空则需要通过下面的方法创建并获取这个单例。
@NonNullpublic static Glide get(@NonNull Context context) {if (glide == null) {GeneratedAppGlideModule annotationGeneratedModule =getAnnotationGeneratedGlideModules(context.getApplicationContext());synchronized (Glide.class) {if (glide == null) {checkAndInitializeGlide(context, annotationGeneratedModule);}}}return glide;}
getAnnotationGeneratedGlideModules方法
其中getAnnotationGeneratedGlideModules方法如下,通过反射实例化AppGlideModule对象并返回,这里的AppGlideModule对象,如果有自定义的话需要进行@GlideModule注解,是不是很熟悉?对就是我们前面自定义前面的GlideApp的一个获取方式,类似Hook的反射替换机制
@Nullable@SuppressWarnings({"unchecked", "TryWithIdenticalCatches", "PMD.UnusedFormalParameter"})private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules(Context context) {GeneratedAppGlideModule result = null;try {Class<GeneratedAppGlideModule> clazz =(Class<GeneratedAppGlideModule>)Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");result =clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext());} catch (ClassNotFoundException e) {if (Log.isLoggable(TAG, Log.WARN)) {Log.w(TAG,"Failed to find GeneratedAppGlideModule. You should include an"+ " annotationProcessor compile dependency on com.github.bumptech.glide:compiler"+ " in your application and a @GlideModule annotated AppGlideModule implementation"+ " or LibraryGlideModules will be silently ignored");}// These exceptions can't be squashed across all versions of Android.} catch (InstantiationException e) {throwIncorrectGlideModule(e);} catch (IllegalAccessException e) {throwIncorrectGlideModule(e);} catch (NoSuchMethodException e) {throwIncorrectGlideModule(e);} catch (InvocationTargetException e) {throwIncorrectGlideModule(e);}return result;}
checkAndInitializeGlide()方法
在运行initGlide()的线程中,一个或多个类可以调用Glide.get(context),如果没有此检查,则这些调用可能会触发无限递归。其中不能在registerComponents()方法中调用Glide.get(context)方法
@GuardedBy("Glide.class")private static void checkAndInitializeGlide(@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {// In the thread running initGlide(), one or more classes may call Glide.get(context).// Without this check, those calls could trigger infinite recursion.//加入isInitializing为是否已经实例化判断if (isInitializing) {throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"+ " use the provided Glide instance instead");}isInitializing = true;initializeGlide(context, generatedAppGlideModule);isInitializing = false;}
initializeGlide()方法
我们继续,进入实例化Glide的方法,也就是这里的initializeGlide()方法
@GuardedBy("Glide.class")@SuppressWarnings("deprecation")private static void initializeGlide(@NonNull Context context,@NonNull GlideBuilder builder,@Nullable GeneratedAppGlideModule annotationGeneratedModule) {Context applicationContext = context.getApplicationContext();//这一步就很巧妙,对于可能不需要实现的List,通过Collections.emptyList()方法可以在不浪费资源的情况下进行创建,然后根据实际需求去进行使用List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();//注解生成的Module为空或者存在Manifest配置的Module,则创建ManifestParser解析Manifest中配置的Moduleif (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {manifestModules = new ManifestParser(applicationContext).parse();}//对Moulde中注册的GlideMoudle和反射拿到的GlideMoudle进行对比过滤,如果双方都存在,那么就过滤Manifest里的Moudleif (annotationGeneratedModule != null&& !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {Set<Class<?>> excludedModuleClasses = annotationGeneratedModule.getExcludedModuleClasses();Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();while (iterator.hasNext()) {com.bumptech.glide.module.GlideModule current = iterator.next();if (!excludedModuleClasses.contains(current.getClass())) {continue;}if (Log.isLoggable(TAG, Log.DEBUG)) {Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);}iterator.remove();}}//打印从manifestModules里新发现的glideMoudleif (Log.isLoggable(TAG, Log.DEBUG)) {for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());}}//初始化RequestManager的工厂对象,赋值给GlideBuilderRequestManagerRetriever.RequestManagerFactory factory =annotationGeneratedModule != null? annotationGeneratedModule.getRequestManagerFactory(): null;builder.setRequestManagerFactory(factory);//通过回调调用外部的applyOption方法//在单例创建之前,也通过这个方法进行加载配置for (com.bumptech.glide.module.GlideModule module : manifestModules) {module.applyOptions(applicationContext, builder);}if (annotationGeneratedModule != null) {annotationGeneratedModule.applyOptions(applicationContext, builder);}//通过build方法进行对Glide对象的创建Glide glide = builder.build(applicationContext);for (com.bumptech.glide.module.GlideModule module : manifestModules) {try {module.registerComponents(applicationContext, glide, glide.registry);} catch (AbstractMethodError e) {throw new IllegalStateException("Attempting to register a Glide v3 module. If you see this, you or one of your"+ " dependencies may be including Glide v3 even though you're using Glide v4."+ " You'll need to find and remove (or update) the offending dependency."+ " The v3 module name is: "+ module.getClass().getName(),e);}}if (annotationGeneratedModule != null) {annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);}//注册Application 生命周期监听。内存紧张时把BitmapPool、MemoryCache和ArrayPool都清空applicationContext.registerComponentCallbacks(glide);Glide.glide = glide;}
GlideBuilder.build()方法
下面就是构建Glide的主要方法,GlideBuilder.build()可以对Glide进行初始化的设置,包括各种执行器,网络请求,动画加载,缓存数据获取等等,以及各类池。
@NonNullGlide build(@NonNull Context context) {//创建执行器,用于从数据源获取数据,例如网络请求if (sourceExecutor == null) {sourceExecutor = GlideExecutor.newSourceExecutor();}//创建执行器,用于从本地缓存获取数据if (diskCacheExecutor == null) {diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();}//创建执行器,用于加载动画帧if (animationExecutor == null) {animationExecutor = GlideExecutor.newAnimationExecutor();}//根据当前机器参数计算需要设置的缓存大小if (memorySizeCalculator == null) {memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();}//默认监听网络的工厂类if (connectivityMonitorFactory == null) {connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();}//创建 Bitmap 池,用于回收LruCache缓存的图片,把图片回收到bitmapPool中,这样下次再创建图片时,可服用该内存,避免连续创建回收内存,造成的内存抖动if (bitmapPool == null) {int size = memorySizeCalculator.getBitmapPoolSize();if (size > 0) {bitmapPool = new LruBitmapPool(size);} else {bitmapPool = new BitmapPoolAdapter();}}//创建数组池if (arrayPool == null) {arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());}//创建内存缓存if (memoryCache == null) {memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());}//创建磁盘缓存if (diskCacheFactory == null) {diskCacheFactory = new InternalCacheDiskCacheFactory(context);}if (engine == null) {engine =new Engine(memoryCache,diskCacheFactory,diskCacheExecutor,sourceExecutor,GlideExecutor.newUnlimitedSourceExecutor(),animationExecutor,isActiveResourceRetentionAllowed);}if (defaultRequestListeners == null) {defaultRequestListeners = Collections.emptyList();} else {defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);}RequestManagerRetriever requestManagerRetriever =new RequestManagerRetriever(requestManagerFactory);return new Glide(context,//上下文engine,//图片加载引擎,负责启动并管理缓存资源memoryCache,// Lru策略的内存缓存池,内存不足时自动清空bitmapPool,//Lru策略的图片池,内存不足时自动清空arrayPool, //Lru策略的数组池,用于读写IO,内存不足时自动清空requestManagerRetriever,//用于感知各组件的生命周期connectivityMonitorFactory,//监听网络的工厂类logLevel,//日志级别defaultRequestOptionsFactory,//默认的RequestOptions生产工厂defaultTransitionOptions,//图片转换处理集合defaultRequestListeners,///默认的Request的监听isLoggingRequestOriginsEnabled,//是否需要请求日志isImageDecoderEnabledForBitmaps);//是否启动图片解码为bitmaps}
这里的一些参数其实可以我们外部进行设置,比如说GlideBuild类里的setImageDecoderEnabledForBitmaps()方法,就可以进行设置,然后通过外部导入。(如果你看不到这个方法,可能是版本比较低,目前我这个版本为4.0的版本)如下所示,就可以使用开启图片解码的功能了,而不需要我们手动再去外面写一个。其实init最终调用的方法也是 initializeGlide()。那么到此一个Glide的创建基本完成,我们回溯到上面,其实会发现还有一个 生命周期监听的方法:registerComponentCallbacks(glide)。但这个方法实际上是在Application类中进行实现是,这里我简要概述下这个方法,就不继续分析源码了。
registerComponentCallbacks()其实是已经建立在framework层内的一个接口方法,主要的作用是为当前的上下建立生命周期的回调,也就是可以进一步的进行生命周期的监听。
GlideBuilder glideBuilder=new GlideBuilder();glideBuilder.setImageDecoderEnabledForBitmaps(true);Glide.init(this,glideBuilder);
load()方法深入了解进程图
那么在上述的with()方法过后,我们要开始对load()方法进行研究。
load()方法
最开始自然是从load()方法开始,这里导入的类型包括Bitmap,Drawable,String,Uri,File,Integer resourceId,URL,byte[] model和Object model,总而言之就是各种能获得图片的类型,包括网络连接,本地文件,资源包里的文件,甚至是图片流的数据。
@NonNull@CheckResult@Overridepublic RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {return asDrawable().load(bitmap);}@NonNull@CheckResult@Overridepublic RequestBuilder<Drawable> load(@Nullable Drawable drawable) {return asDrawable().load(drawable);}@NonNull@CheckResult@Overridepublic RequestBuilder<Drawable> load(@Nullable String string) {return asDrawable().load(string);}@NonNull@CheckResult@Overridepublic RequestBuilder<Drawable> load(@Nullable Uri uri) {return asDrawable().load(uri);}@NonNull@CheckResult@Overridepublic RequestBuilder<Drawable> load(@Nullable File file) {return asDrawable().load(file);}@SuppressWarnings("deprecation")@NonNull@CheckResult@Overridepublic RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {return asDrawable().load(resourceId);}@SuppressWarnings("deprecation")@CheckResult@Override@Deprecatedpublic RequestBuilder<Drawable> load(@Nullable URL url) {return asDrawable().load(url);}@NonNull@CheckResult@Overridepublic RequestBuilder<Drawable> load(@Nullable byte[] model) {return asDrawable().load(model);}@NonNull@CheckResult@Overridepublic RequestBuilder<Drawable> load(@Nullable Object model) {return asDrawable().load(model);}
但无论是那一种类型都会通过asDrawable().load()方法传入,所以我们接下来看看这个方法。
asDrawable().load()方法
其实在这个方法里主要做的就是再次调用loadGeneric()方法,返回一个RequestBuilder对象,那么唯一不同点就在于会对model数据进行一个优化处理,以及返回的对象不同。其实这里主要是知道我们的一个数据来源,也就是loadGeneric()方法
@NonNull@CheckResult@SuppressWarnings("unchecked")@Overridepublic RequestBuilder<TranscodeType> load(@Nullable Object model) {return loadGeneric(model);}
//设置isModelSet表明已经知道了数据来源@NonNullprivate RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {this.model = model;isModelSet = true;return this;}@NonNull@CheckResult@Overridepublic RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {return loadGeneric(bitmap).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));}@NonNull@CheckResult@Overridepublic RequestBuilder<TranscodeType> load(@Nullable Drawable drawable) {return loadGeneric(drawable).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));}@NonNull@Override@CheckResultpublic RequestBuilder<TranscodeType> load(@Nullable String string) {return loadGeneric(string);}@NonNull@CheckResult@Overridepublic RequestBuilder<TranscodeType> load(@Nullable Uri uri) {return loadGeneric(uri);}@NonNull@CheckResult@Overridepublic RequestBuilder<TranscodeType> load(@Nullable File file) {return loadGeneric(file);}@NonNull@CheckResult@Overridepublic RequestBuilder<TranscodeType> load(@RawRes @DrawableRes @Nullable Integer resourceId) {return loadGeneric(resourceId).apply(signatureOf(AndroidResourceSignature.obtain(context)));}@Deprecated@CheckResult@Overridepublic RequestBuilder<TranscodeType> load(@Nullable URL url) {return loadGeneric(url);}//对moudle数据进行一个磁盘策略和跳过缓存的判断,如果需要跳过缓存策略则进行跳过。@NonNull@CheckResult@Overridepublic RequestBuilder<TranscodeType> load(@Nullable byte[] model) {RequestBuilder<TranscodeType> result = loadGeneric(model);if (!result.isDiskCacheStrategySet()) {result = result.apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));}if (!result.isSkipMemoryCacheSet()) {result = result.apply(skipMemoryCacheOf(true /*skipMemoryCache*/));}return result;}
当然,如果是一些其他类型的图片,那么这里的加载策略采用的就是首先检测是否关闭磁盘缓存策略,然后通过apply进行
BaseRequestOptions apply()方法
其实在apply方法之前有一个判空的方法,对传入值是否为空进行判断checkNotNull() 。而判空之后在apply()方法里进行载入文件的初始化。在这里主要通过逻辑判断对一些图片资源的相关设置进行初始化流程,包括大小,路径等等,到此一个load的基本流程也基本结束了。最后这里返回的对象虽然是泛型,但在使用into前是RequestBuild对象。
a &= b; // 其实就是a = a & b; 其中&是按位与运算
a |= b; // 其实就是a = a | b; 其中|是按位或运算
~ 逻辑非
@NonNull@CheckResultpublic T apply(@NonNull BaseRequestOptions<?> o) {if (isAutoCloneEnabled) {return clone().apply(o);}BaseRequestOptions<?> other = o;if (isSet(other.fields, SIZE_MULTIPLIER)) {sizeMultiplier = other.sizeMultiplier;}if (isSet(other.fields, USE_UNLIMITED_SOURCE_GENERATORS_POOL)) {useUnlimitedSourceGeneratorsPool = other.useUnlimitedSourceGeneratorsPool;}if (isSet(other.fields, USE_ANIMATION_POOL)) {useAnimationPool = other.useAnimationPool;}if (isSet(other.fields, DISK_CACHE_STRATEGY)) {diskCacheStrategy = other.diskCacheStrategy;}if (isSet(other.fields, PRIORITY)) {priority = other.priority;}if (isSet(other.fields, ERROR_PLACEHOLDER)) {errorPlaceholder = other.errorPlaceholder;errorId = 0;fields &= ~ERROR_ID;}if (isSet(other.fields, ERROR_ID)) {errorId = other.errorId;errorPlaceholder = null;fields &= ~ERROR_PLACEHOLDER;}if (isSet(other.fields, PLACEHOLDER)) {placeholderDrawable = other.placeholderDrawable;placeholderId = 0;fields &= ~PLACEHOLDER_ID;}if (isSet(other.fields, PLACEHOLDER_ID)) {placeholderId = other.placeholderId;placeholderDrawable = null;fields &= ~PLACEHOLDER;}if (isSet(other.fields, IS_CACHEABLE)) {isCacheable = other.isCacheable;}if (isSet(other.fields, OVERRIDE)) {overrideWidth = other.overrideWidth;overrideHeight = other.overrideHeight;}if (isSet(other.fields, SIGNATURE)) {signature = other.signature;}if (isSet(other.fields, RESOURCE_CLASS)) {resourceClass = other.resourceClass;}if (isSet(other.fields, FALLBACK)) {fallbackDrawable = other.fallbackDrawable;fallbackId = 0;fields &= ~FALLBACK_ID;}if (isSet(other.fields, FALLBACK_ID)) {fallbackId = other.fallbackId;fallbackDrawable = null;fields &= ~FALLBACK;}if (isSet(other.fields, THEME)) {theme = other.theme;}if (isSet(other.fields, TRANSFORMATION_ALLOWED)) {isTransformationAllowed = other.isTransformationAllowed;}if (isSet(other.fields, TRANSFORMATION_REQUIRED)) {isTransformationRequired = other.isTransformationRequired;}if (isSet(other.fields, TRANSFORMATION)) {transformations.putAll(other.transformations);isScaleOnlyOrNoTransform = other.isScaleOnlyOrNoTransform;}if (isSet(other.fields, ONLY_RETRIEVE_FROM_CACHE)) {onlyRetrieveFromCache = other.onlyRetrieveFromCache;}// Applying options with dontTransform() is expected to clear our transformations.if (!isTransformationAllowed) {transformations.clear();fields &= ~TRANSFORMATION;isTransformationRequired = false;fields &= ~TRANSFORMATION_REQUIRED;isScaleOnlyOrNoTransform = true;}fields |= other.fields;options.putAll(other.options);return selfOrThrowIfLocked();}
附上isSet()方法:只有两个参数都不为0时返回true.
private static boolean isSet(int fields, int flag) {return (fields & flag) != 0;}
into()方法深入了解进程图
那么很快就来到了Glide的第三个方法into,那么这一块其实看看这个图也会发现挺恐怖的,但慢慢来看也是有一些意思。
into()方法
其中assertMainThread()是对是否在主线程调用的一个判断,如果在主线程进行调用,那么就继续执行,不然抛出illega异常,因为加载图像是UI操作,所以需要在主线程上进行。同样Preconditions.checkNotNull(view)是检测view是否在当前界面存在。之后就是对比例转换的一个判断。最后在into构造中传入三个参数,我们先看第一个参数glideContext.buildImageViewTarget(view, transcodeClass)。
@NonNullpublic ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {Util.assertMainThread();//判断是否在主线程进行Preconditions.checkNotNull(view);//view是否为空判断BaseRequestOptions<?> requestOptions = this;if (!requestOptions.isTransformationSet()&& requestOptions.isTransformationAllowed()&& view.getScaleType() != null) {//克隆此方法,以便如果我们使用此RequestBuilder加载到View中,然后加载到其他目标中,则不会保留基于先前View的比例类型应用的转换,对于缩放类型的一个判断.// Clone in this method so that if we use this RequestBuilder to load into a View and then// into a different target, we don't retain the transformation applied based on the previous// View's scale type.switch (view.getScaleType()) {case CENTER_CROP:requestOptions = requestOptions.clone().optionalCenterCrop();break;case CENTER_INSIDE:requestOptions = requestOptions.clone().optionalCenterInside();break;case FIT_CENTER:case FIT_START:case FIT_END:requestOptions = requestOptions.clone().optionalFitCenter();break;case FIT_XY:requestOptions = requestOptions.clone().optionalCenterInside();break;case CENTER:case MATRIX:default:// Do nothing.}}return into(//这个transcodeClass是指的drawable或bitmap,就是前面as函数传入的类型glideContext.buildImageViewTarget(view, transcodeClass),/*targetListener=*/ null,requestOptions,//在主线程,也就是通过主线程的Handler 执行Executors.mainThreadExecutor());}
glideContext.buildImageViewTarget()
首先通过该方法进入初始化好的imageViewTargetFactory工厂类进行函数构造。而初始此工厂的方法在initializeGlide()里,也就是我们最早的with()方法。
@NonNullpublic <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {return imageViewTargetFactory.buildTarget(imageView, transcodeClass);}
然后通过进入改工厂类调用buildTarget()方法进行对传入目标进行初步构造。构造方法如下:
public class ImageViewTargetFactory {@NonNull@SuppressWarnings("unchecked")public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) {if (Bitmap.class.equals(clazz)) {//如果为Bitmap类型return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);} else if (Drawable.class.isAssignableFrom(clazz)) {//如果为Drawable类型return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);} else {throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");}}
}
在初步构造方法里对传入类进行判断也就是前面的transcodeClass,然后进行选择显示一个Bitmap或Drawable 到 ImageView 中,如果二者都不是,那么这个异常是跑不了了。
RequestBuild into()
这里我们回到上面的into构造函数,这里有一个isModelSet作为是否有图片数据导入的依据,也就是说在进行into之前必须进行load,不然看到会出现异常,后面通过对request进行请求和获取操作能够达到图片加载的目的。
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,@Nullable RequestListener<TranscodeType> targetListener,BaseRequestOptions<?> options,Executor callbackExecutor) {Preconditions.checkNotNull(target);if (!isModelSet) {throw new IllegalArgumentException("You must call #load() before calling #into()");}
//创建请求,用于显示图片。图片有可能是从缓存中,也有可能是从网络获取Request request = buildRequest(target, targetListener, options, callbackExecutor);
//获取该目标 对应的request,和当前的request进行比较Request previous = target.getRequest();if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {//如果请求完成,则重新开始将确保结果重新传递,触发RequestListeners和Targets。//如果请求失败,则重新开始将重新启动请求,从而给它另一个完成的机会。如果请求已经运行,我们可以让它继续运行而不会中断。// If the request is completed, beginning again will ensure the result is re-delivered,// triggering RequestListeners and Targets. If the request is failed, beginning again will// restart the request, giving it another chance to complete. If the request is already// running, we can let it continue running without interruption.if (!Preconditions.checkNotNull(previous).isRunning()) {// Use the previous request rather than the new one to allow for optimizations like skipping// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions// that are done in the individual Request.previous.begin();}return target;}requestManager.clear(target); //把当前的request,设置到target(ViewTarget)target.setRequest(request);//进行图片请求操作requestManager.track(target, request);return target;
RequestBuild buildRequest()
那我们来详细看看这个request是怎么创建的,而这个方法就是buildRequest().
private Request buildRequest(Target<TranscodeType> target,@Nullable RequestListener<TranscodeType> targetListener,BaseRequestOptions<?> requestOptions,Executor callbackExecutor) {return buildRequestRecursive(/*requestLock=*/ new Object(),target,targetListener,/*parentCoordinator=*/ null,transitionOptions,requestOptions.getPriority(),requestOptions.getOverrideWidth(),requestOptions.getOverrideHeight(),requestOptions,callbackExecutor);}
进一步执行buildRequestRecursive方法获取request请求。
private Request buildRequestRecursive(Object requestLock,Target<TranscodeType> target,@Nullable RequestListener<TranscodeType> targetListener,@Nullable RequestCoordinator parentCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight,BaseRequestOptions<?> requestOptions,Executor callbackExecutor) {// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.ErrorRequestCoordinator errorRequestCoordinator = null;//进行错误调节器的一个更新if (errorBuilder != null) {errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);parentCoordinator = errorRequestCoordinator;}Request mainRequest =buildThumbnailRequestRecursive(requestLock,target,targetListener,parentCoordinator,transitionOptions,priority,overrideWidth,overrideHeight,requestOptions,callbackExecutor);if (errorRequestCoordinator == null) {return mainRequest;}int errorOverrideWidth = errorBuilder.getOverrideWidth();int errorOverrideHeight = errorBuilder.getOverrideHeight();if (Util.isValidDimensions(overrideWidth, overrideHeight) && !errorBuilder.isValidOverride()) {errorOverrideWidth = requestOptions.getOverrideWidth();errorOverrideHeight = requestOptions.getOverrideHeight();}Request errorRequest =errorBuilder.buildRequestRecursive(requestLock,target,targetListener,errorRequestCoordinator,errorBuilder.transitionOptions,errorBuilder.getPriority(),errorOverrideWidth,errorOverrideHeight,errorBuilder,callbackExecutor);errorRequestCoordinator.setRequests(mainRequest, errorRequest);return errorRequestCoordinator;}
RequestBuild buildThumbnailRequestRecursive()
那么我们再来细细看看这个方法,在buildThumbnailRequestRecursive方法里开始是进行缩略图的创建,包括是否采取缩略图,是否需要设置缩略图的大小,也就是对缩略图请求的一个策略设置。但最终都会执行obtainRequest。
private Request buildThumbnailRequestRecursive(Object requestLock,Target<TranscodeType> target,RequestListener<TranscodeType> targetListener,@Nullable RequestCoordinator parentCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight,BaseRequestOptions<?> requestOptions,Executor callbackExecutor) {if (thumbnailBuilder != null) {// Recursive case: contains a potentially recursive thumbnail request builder.//在这里可能存在潜在的递归案例,所以可用来避免这样的递归请求if (isThumbnailBuilt) {throw new IllegalStateException("You cannot use a request as both the main request and a "+ "thumbnail, consider using clone() on the request(s) passed to thumbnail()");}TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions =thumbnailBuilder.transitionOptions;//默认情况下,将我们的过渡应用于缩略图请求,但要避免覆盖可能已明确应用于缩略图请求的自定义选项。// Apply our transition by default to thumbnail requests but avoid overriding custom options// that may have been applied on the thumbnail request explicitly.if (thumbnailBuilder.isDefaultTransitionOptionsSet) {thumbTransitionOptions = transitionOptions;}Priority thumbPriority =thumbnailBuilder.isPrioritySet()? thumbnailBuilder.getPriority(): getThumbnailPriority(priority);int thumbOverrideWidth = thumbnailBuilder.getOverrideWidth();int thumbOverrideHeight = thumbnailBuilder.getOverrideHeight();//如果宽度和高度均大于0和/或等于int SIZE_ORIGINAL即Integer.MIN_VALUE;if (Util.isValidDimensions(overrideWidth, overrideHeight)&& !thumbnailBuilder.isValidOverride()) {thumbOverrideWidth = requestOptions.getOverrideWidth();thumbOverrideHeight = requestOptions.getOverrideHeight();}//缩略图请求协调器ThumbnailRequestCoordinator coordinator =new ThumbnailRequestCoordinator(requestLock, parentCoordinator);Request fullRequest =obtainRequest(requestLock,target,targetListener,requestOptions,coordinator,transitionOptions,priority,overrideWidth,overrideHeight,callbackExecutor);isThumbnailBuilt = true;// Recursively generate thumbnail requests.//因为调用thumbnail 传递GlideRequest 参数时,这个请求也可能设置了缩略图和错误图(也就是缩略图的缩略图),所以这里进行递归创建RequestRequest thumbRequest =thumbnailBuilder.buildRequestRecursive(requestLock,target,targetListener,coordinator,thumbTransitionOptions,thumbPriority,thumbOverrideWidth,thumbOverrideHeight,thumbnailBuilder,callbackExecutor);isThumbnailBuilt = false;coordinator.setRequests(fullRequest, thumbRequest);return coordinator;} else if (thumbSizeMultiplier != null) {//缩略图乘法器会生成缩略图请求,但不能递归// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.ThumbnailRequestCoordinator coordinator =new ThumbnailRequestCoordinator(requestLock, parentCoordinator);Request fullRequest =obtainRequest(requestLock,target,targetListener,requestOptions,coordinator,transitionOptions,priority,overrideWidth,overrideHeight,callbackExecutor);BaseRequestOptions<?> thumbnailOptions =requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);Request thumbnailRequest =obtainRequest(requestLock,target,targetListener,thumbnailOptions,coordinator,transitionOptions,getThumbnailPriority(priority),overrideWidth,overrideHeight,callbackExecutor);coordinator.setRequests(fullRequest, thumbnailRequest);return coordinator;} else {//无缩略图// Base case: no thumbnail.return obtainRequest(requestLock,target,targetListener,requestOptions,parentCoordinator,transitionOptions,priority,overrideWidth,overrideHeight,callbackExecutor);}}
RequestBuild obtainRequest()
不过最终都会执行到obtainRequest()这个方法,进入执行SingleRequest内的obtain方法。
private Request obtainRequest(Object requestLock,Target<TranscodeType> target,RequestListener<TranscodeType> targetListener,BaseRequestOptions<?> requestOptions,RequestCoordinator requestCoordinator,TransitionOptions<?, ? super TranscodeType> transitionOptions,Priority priority,int overrideWidth,int overrideHeight,Executor callbackExecutor) {return SingleRequest.obtain(context,glideContext,requestLock,model,transcodeClass,requestOptions,overrideWidth,overrideHeight,priority,target,targetListener,requestListeners,requestCoordinator,glideContext.getEngine(),transitionOptions.getTransitionFactory(),callbackExecutor);}
也就是构建一个SingleRequest类
public static <R> SingleRequest<R> obtain(Context context,GlideContext glideContext,Object requestLock,Object model,Class<R> transcodeClass,BaseRequestOptions<?> requestOptions,int overrideWidth,int overrideHeight,Priority priority,Target<R> target,RequestListener<R> targetListener,@Nullable List<RequestListener<R>> requestListeners,RequestCoordinator requestCoordinator,Engine engine,TransitionFactory<? super R> animationFactory,Executor callbackExecutor) {return new SingleRequest<>(context,glideContext,requestLock,model,transcodeClass,requestOptions,overrideWidth,overrideHeight,priority,target,targetListener,requestListeners,requestCoordinator,engine,animationFactory,callbackExecutor);}
SingleRequest
最后这里其实是将这些进行赋值生成,那么我们来大致看看这些都是什么吧。另外这些对象会绑上请求锁,仅在初始化请求或将请求返回到对象池时才对变量进行突变。
private SingleRequest(Context context,GlideContext glideContext, //Glide中所有加载的全局上下文,其中包含并公开了加载资源所需的各种注册表和类@NonNull Object requestLock, //请求锁仅初始化或返回对象池的时候才允许进行变量突变@Nullable Object model, //model数据Class<R> transcodeClass, //编码类BaseRequestOptions<?> requestOptions, //之前aplly方法应用的请求选项int overrideWidth, //覆写的宽int overrideHeight, //覆写的高Priority priority, //优先级Target<R> target, //目标类@Nullable RequestListener<R> targetListener, //请求状态的监听,在加入请求时运行在后台监听图片加载请求@Nullable List<RequestListener<R>> requestListeners, //请求监听列表,可能不止一个请求RequestCoordinator requestCoordinator, //请求协调,避免死锁Engine engine, //负责启动负载以及管理活动和缓存的资源TransitionFactory<? super R> animationFactory, //动画化工厂,根据请求zhuanExecutor callbackExecutor) { //回调执行者this.requestLock = requestLock; this.context = context;this.glideContext = glideContext;this.model = model;this.transcodeClass = transcodeClass;this.requestOptions = requestOptions;this.overrideWidth = overrideWidth;this.overrideHeight = overrideHeight;this.priority = priority; this.target = target; this.targetListener = targetListener;this.requestListeners = requestListeners;this.requestCoordinator = requestCoordinator;this.engine = engine;this.animationFactory = animationFactory;this.callbackExecutor = callbackExecutor;status = Status.PENDING;if (requestOrigin == null && glideContext.isLoggingRequestOriginsEnabled()) {requestOrigin = new RuntimeException("Glide request origin trace");}}
这时候我们回到前面的into构造函数里,在拿到request后会执行下面几个方法。这里简单在代码上回顾下。
requestManager.clear(target);//清理targettarget.setRequest(request); //设置请求requestManager.track(target, request);//开始请求
RequestManager track()
这里我们详细看看开始请求的方法,首先当然这个请求是在同步锁里执行的,不能多请求同时进行,避免数据冲突。
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {targetTracker.track(target); //给targets里添加targetrequestTracker.runRequest(request); //开始跟踪给定的请求}
其中如果请求没有中断就执行请求,就会执行begin方法,否则会执行clear方法并将请求放入待执行列表中。
public void runRequest(@NonNull Request request) {requests.add(request);//填充目标(ImageView)所在的activity Fragment stop后,isPaused就是trueif (!isPaused) {//如果不是暂停,就开始执行request.begin();} else {request.clear();if (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "Paused, delaying request");}//加入待执行列表pendingRequests.add(request);}}
RequestManager begin()
那么这里我们来看看begin()方法,当然这里会提供锁住请求锁对象进行顺序执行,而不是并发执行,以及其他的一些判断,最终都会走到onSizeReady方法进行真正的请求。
@Overridepublic void begin() {synchronized (requestLock) {0assertNotCallingCallbacks();//这里翻译过来是避免难以理解的错误,内部提供判断isCallingCallbacks进行判断,用以标志是否在RequestListener或Target回调中启动或清除负载这种会产生加载图片错误的操作。stateVerifier.throwIfRecycled();//如果我们认为对象已回收且处于非活动状态,则会引发异常startTime = LogTime.getLogTime();if (model == null) {if (Util.isValidDimensions(overrideWidth, overrideHeight)) {width = overrideWidth;height = overrideHeight;}// Only log at more verbose log levels if the user has set a fallback drawable, because// fallback Drawables indicate the user expects null models occasionally.int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;onLoadFailed(new GlideException("Received null model"), logLevel);return;}if (status == Status.RUNNING) {throw new IllegalArgumentException("Cannot restart a running request");}// If we're restarted after we're complete (usually via something like a notifyDataSetChanged// that starts an identical request into the same Target or View), we can simply use the// resource and size we retrieved the last time around and skip obtaining a new size, starting// a new load etc. This does mean that users who want to restart a load because they expect// that the view size has changed will need to explicitly clear the View or Target before// starting the new load.if (status == Status.COMPLETE) {onResourceReady(resource, DataSource.MEMORY_CACHE);return;}// Restarts for requests that are neither complete nor running can be treated as new requests// and can run again from the beginning.status = Status.WAITING_FOR_SIZE;if (Util.isValidDimensions(overrideWidth, overrideHeight)) {// 当使用override() API为图片指定了一个固定的宽高时直接执行onSizeReady,// 最终的核心处理位于onSizeReadyonSizeReady(overrideWidth, overrideHeight);} else {// 根据imageView的宽高算出图片的宽高,最终也会走到onSizeReadytarget.getSize(this);}if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)&& canNotifyStatusChanged()) {// 预先加载设置的缩略图target.onLoadStarted(getPlaceholderDrawable());}if (IS_VERBOSE_LOGGABLE) {logV("finished run method in " + LogTime.getElapsedMillis(startTime));}}}
RequestManager onSizeReady()
在此首先绝对不能直接调用的回调方法,对图片能否加载进行一系列判断后,利用engine封装了各种策略和引擎等等。那么在load中对于图片缓存就进行了真正的操作。下面我们来细看下load方法
@Overridepublic void onSizeReady(int width, int height) {stateVerifier.throwIfRecycled();synchronized (requestLock) {if (IS_VERBOSE_LOGGABLE) {logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));}if (status != Status.WAITING_FOR_SIZE) {return;}status = Status.RUNNING;//拿到request的设置进行对是否能缩放尺寸进行设置float sizeMultiplier = requestOptions.getSizeMultiplier();this.width = maybeApplySizeMultiplier(width, sizeMultiplier);this.height = maybeApplySizeMultiplier(height, sizeMultiplier);if (IS_VERBOSE_LOGGABLE) {logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));}//这里的engine 是在创建Glide的时候,build() 创建的,engine封装了各种Executor,内存缓存等loadStatus =engine.load(glideContext,model,requestOptions.getSignature(),this.width,this.height,requestOptions.getResourceClass(),transcodeClass,priority,requestOptions.getDiskCacheStrategy(),requestOptions.getTransformations(),requestOptions.isTransformationRequired(),requestOptions.isScaleOnlyOrNoTransform(),requestOptions.getOptions(),requestOptions.isMemoryCacheable(),requestOptions.getUseUnlimitedSourceGeneratorsPool(),requestOptions.getUseAnimationPool(),requestOptions.getOnlyRetrieveFromCache(),this,callbackExecutor);// This is a hack that's only useful for testing right now where loads complete synchronously// even though under any executor running on any thread but the main thread, the load would// have completed asynchronously.//这是一个hack,仅对于现在测试加载同步完成的有用,即使在主线程以外的任何线程上运行的任何执行程序下,加载都会异步完成。if (status != Status.RUNNING) {loadStatus = null;}if (IS_VERBOSE_LOGGABLE) {logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));}}}
Engine load()
在此方法中进行给的参数的加载。也是在这里进行缓存的操作,通过查找到的key进行缓存访查找,然后根据是否查找到这个缓存进行缓存的判断使用。
public <R> LoadStatus load(GlideContext glideContext,Object model,Key signature,int width,int height,Class<?> resourceClass,Class<R> transcodeClass,Priority priority,DiskCacheStrategy diskCacheStrategy,Map<Class<?>, Transformation<?>> transformations,boolean isTransformationRequired,boolean isScaleOnlyOrNoTransform,Options options,boolean isMemoryCacheable,boolean useUnlimitedSourceExecutorPool,boolean useAnimationPool,boolean onlyRetrieveFromCache,ResourceCallback cb,Executor callbackExecutor) {long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;//生成缓存key,以后就根据这个key,在缓存中查找EngineKey key =keyFactory.buildKey(model,signature,width,height,transformations,resourceClass,transcodeClass,options);EngineResource<?> memoryResource;synchronized (this) {memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);if (memoryResource == null) {return waitForExistingOrStartNewJob(glideContext,model,signature,width,height,resourceClass,transcodeClass,priority,diskCacheStrategy,transformations,isTransformationRequired,isScaleOnlyOrNoTransform,options,isMemoryCacheable,useUnlimitedSourceExecutorPool,useAnimationPool,onlyRetrieveFromCache,cb,callbackExecutor,key,startTime);}}// Avoid calling back while holding the engine lock, doing so makes it easier for callers to// deadlock.//避免在按住引擎锁的同时回掉,这样可以使调用者更容易死锁cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);return null;}
其中对于查找三级缓存的操作在loadFromMemory方法中,如下:
三级缓存:
1、弱引用缓存,使用弱引用,来缓存图片,图片被回收后,会保存到内存缓存中。
2、内存缓存LruCache(默认是在创建Glide的时候创建的,也可自定义), 如果弱引用缓存找不到图片,就从内存缓存中查找,找到图片后,删除内存缓存(防止因Lru的策略,图片正在使用,但是被回收掉的问题)
3、磁盘缓存 ,上面两级缓存都没有图片,如果在磁盘缓存中找到,就把图片加载后,放到弱引用缓存中。磁盘缓存数据的种类有两种,一种是缓存源数据,这种数据需要经过解析才能得到图片。一种是图片数据,直接加载进来就可以用的。可以通过diskCacheStrategyOf 来自由选择如何缓存
@Nullableprivate EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {if (!isMemoryCacheable) {return null;}
//检查弱引用缓存是否有目标图片EngineResource<?> active = loadFromActiveResources(key);if (active != null) {if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from active resources", startTime, key);}return active;}
//检查内存的弱引用缓存是否有目标图片EngineResource<?> cached = loadFromCache(key);if (cached != null) {if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Loaded resource from cache", startTime, key);}return cached;}return null;}
Engine waitForExistingOrStartNewJob()
细心的话会发现在三级缓存内,有一级磁盘缓存没有进行操作,那是因为这个操作被放入到了waitForExistingOrStartNewJob方法中,也就是如果在弱引用缓存无法找到就执行磁盘内的缓存,因为查询这个磁盘的访问时间也较长和创建新的缓存放到同一个方法内(前几个版本是都在 load方法内进行缓存判断获取)。
private <R> LoadStatus waitForExistingOrStartNewJob(GlideContext glideContext,Object model,Key signature,int width,int height,Class<?> resourceClass,Class<R> transcodeClass,Priority priority,DiskCacheStrategy diskCacheStrategy,Map<Class<?>, Transformation<?>> transformations,boolean isTransformationRequired,boolean isScaleOnlyOrNoTransform,Options options,boolean isMemoryCacheable,boolean useUnlimitedSourceExecutorPool,boolean useAnimationPool,boolean onlyRetrieveFromCache,ResourceCallback cb,Executor callbackExecutor,EngineKey key,long startTime) {
//在弱引用和内存缓存中,都没有找到图片,就执行任务。这个任务,会现在磁盘缓存中查找,因为磁盘读取耗时较大,所以放在任务线程中EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);if (current != null) {current.addCallback(cb, callbackExecutor);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Added to existing load", startTime, key);}return new LoadStatus(cb, current);}EngineJob<R> engineJob =engineJobFactory.build(key,isMemoryCacheable,useUnlimitedSourceExecutorPool,useAnimationPool,onlyRetrieveFromCache);DecodeJob<R> decodeJob =decodeJobFactory.build(glideContext,model,key,signature,width,height,resourceClass,transcodeClass,priority,diskCacheStrategy,transformations,isTransformationRequired,isScaleOnlyOrNoTransform,onlyRetrieveFromCache,options,engineJob);
// 放在Jobs内部维护的HashMap中jobs.put(key, engineJob);
// 注册ResourceCallback接口,就是在成功获取图片后,需要显示到ImageView 上的回调,这个接口回调到SingleRequest 中engineJob.addCallback(cb, callbackExecutor);//开始执行engineJob.start(decodeJob);if (VERBOSE_IS_LOGGABLE) {logWithTimeAndKey("Started new load", startTime, key);}return new LoadStatus(cb, engineJob);}
Engine start()
这边开启
public synchronized void start(DecodeJob<R> decodeJob) {this.decodeJob = decodeJob;//若能从磁盘缓存获取数据,就使用diskCacheExecutor//否则在根据其他的条件判断使用哪个ExecutorGlideExecutor executor =decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();executor.execute(decodeJob);}
如果此作业将尝试从磁盘缓存中解码资源,则返回true;如果它将始终从源解码,则返回false
boolean willDecodeFromCache() {//先看下面的getNextStage 分析Stage firstStage = getNextStage(Stage.INITIALIZE);//那这里就很明了了,如果可以从磁盘缓存读取,就返回truereturn firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;}
通过判断当前的策略执行对应获取缓存的方法。
private Stage getNextStage(Stage current) {switch (current) {//当前是初始阶段,则看磁盘缓存策略,是否可以在磁盘中获取资源缓存(也就是解析后的缓存)case INITIALIZE:return diskCacheStrategy.decodeCachedResource()? Stage.RESOURCE_CACHE: getNextStage(Stage.RESOURCE_CACHE);//当前是资源缓存,看下一步能不能从磁盘缓存中获取源数据缓存case RESOURCE_CACHE:return diskCacheStrategy.decodeCachedData()? Stage.DATA_CACHE: getNextStage(Stage.DATA_CACHE);// Skip loading from source if the user opted to only retrieve the resource from cache.//当前是数据缓存,下一步能不能从源数据处获取数据,例如从服务器获取 case DATA_CACHE:// Skip loading from source if the user opted to only retrieve the resource from cache.return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;case SOURCE:case FINISHED:return Stage.FINISHED;default:throw new IllegalArgumentException("Unrecognized stage: " + current);}}
之后拿到策略后就会执行executor.execute(decodeJob); 进入到 Decodejob 的run 函数,接着调用runWrapped。
DecodeJob runWrapped()
在拿去到策略后,会初始化runReason进入阶段状态执行,然后通过执行runGenerators(),然后进入
private void runWrapped() {
//初始化之后第一次运行时 runReason 为 INITIALIZEswitch (runReason) {case INITIALIZE://获取下一阶段的状态stage = getNextStage(Stage.INITIALIZE);//根据下一阶段状态,判断具体有哪个类执行currentGenerator = getNextGenerator();runGenerators();break;case SWITCH_TO_SOURCE_SERVICE:runGenerators();break;case DECODE_DATA:decodeFromRetrievedData();break;default:throw new IllegalStateException("Unrecognized run reason: " + runReason);} }private DataFetcherGenerator getNextGenerator() {switch (stage) {case RESOURCE_CACHE://从磁盘缓存获取资源数据return new ResourceCacheGenerator(decodeHelper, this);case DATA_CACHE://从数据缓存获取资源数据return new DataCacheGenerator(decodeHelper, this);case SOURCE://从数据来源获取资源数据return new SourceGenerator(decodeHelper, this);case FINISHED:return null;default:throw new IllegalStateException("Unrecognized stage: " + stage);}}private void runGenerators() {currentThread = Thread.currentThread();startFetchTime = LogTime.getLogTime();boolean isStarted = false;while (!isCancelled&& currentGenerator != null&& !(isStarted = currentGenerator.startNext())) {//这里执行当前的Generatorstage = getNextStage(stage);currentGenerator = getNextGenerator();if (stage == Stage.SOURCE) {//即使在这里取消了作业,仍需要对其进行计划,以便可以自行清理reschedule();return;}}// We've run out of stages and generators, give up.//我们已经用完了空间和生成器,放弃了if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {notifyFailed();}//否则,生成器将开始新的加载,我们希望在onDataFetcherReady中对其进行回调// Otherwise a generator started a new load and we expect to be called back in// onDataFetcherReady.}
SourceGenerator startNext
在startNext()方法里将进行查看是否能够通过loadData获取到缓存的数据,如果存在就把该缓存调到磁盘里
@Overridepublic boolean startNext() {//第一次 从源数据获取数据时,是不会执行到这里的//从下面的分析可知,等下次有数据时,也会调用到这里,就把数据缓存到磁盘if (dataToCache != null) {Object data = dataToCache;dataToCache = null;//放入缓存 cacheData(data);}if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {return true;}sourceCacheGenerator = null;loadData = null;boolean started = false;while (!started && hasNextModelLoader()) {//helper.getLoadData() 获取所有符合条件的ModelLoader,这些ModelLoader 包括默认的和自定义的//这里的符合条件,也就是ModelLoader 中的handles函数是否返回true,再说直白点,就是判断在load()传入的对象类型,是否可以被ModelLoader所处理loadData = helper.getLoadData().get(loadDataListIndex++);if (loadData != null&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {started = true;startNextLoad(loadData);}}return started;}private void startNextLoad(final LoadData<?> toStart) {//通过LoadData对象内部的 fetcher ,来进行实际的请求操作(例如发起网络请求)loadData.fetcher.loadData(helper.getPriority(),new DataCallback<Object>() {@Overridepublic void onDataReady(@Nullable Object data) {if (isCurrentRequest(toStart)) {onDataReadyInternal(toStart, data);}}@Overridepublic void onLoadFailed(@NonNull Exception e) {if (isCurrentRequest(toStart)) {onLoadFailedInternal(toStart, e);}}});}
DecodeHelper getLoadData()
至于其中的loadData对象我们通过getloadData()方法近获取。
List<LoadData<?>> getLoadData() {if (!isLoadDataSet) {isLoadDataSet = true;loadData.clear();//获取已注册的加载器中所有可以加载当前模型的加载器List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);//noinspection ForLoopReplaceableByForEach to improve perffor (int i = 0, size = modelLoaders.size(); i < size; i++) {ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);//每一个ModeLoader 都有一个内部类LoadData,通过函数buildLoadData 来创建LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);if (current != null) {loadData.add(current);}}}return loadData;}
ModelLoader getModelLoaders()方法
通过getModelLoaders()方法可用对拿到的loaders的列表进行过滤,然后进行添加到filteredLoaders的列表里,然后作为最后的值进行返回。
@NonNullpublic <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));if (modelLoaders.isEmpty()) {throw new NoModelLoaderAvailableException(model);}int size = modelLoaders.size();boolean isEmpty = true;List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();//noinspection ForLoopReplaceableByForEach to improve perffor (int i = 0; i < size; i++) {ModelLoader<A, ?> loader = modelLoaders.get(i);if (loader.handles(model)) {//通过 ModelLoader#handles 方法判断加载器是否可以处理当前类型的数据(这个数据时通过load传递进来的),返回所有可以处理的加载器 if (isEmpty) {filteredLoaders = new ArrayList<>(size - i);isEmpty = false;}filteredLoaders.add(loader);}}if (filteredLoaders.isEmpty()) {throw new NoModelLoaderAvailableException(model, modelLoaders);}return filteredLoaders;}
ModelLoader buildLoadData()方法
其中每一个ModeLoader 都有一个内部类LoadData,通过函数buildLoadData(MultiModelLoader类)来创建,也就是如下的方法获取loadData
@Overridepublic LoadData<Data> buildLoadData(@NonNull Model model, int width, int height, @NonNull Options options) {Key sourceKey = null;int size = modelLoaders.size();List<DataFetcher<Data>> fetchers = new ArrayList<>(size);//noinspection ForLoopReplaceableByForEach to improve perffor (int i = 0; i < size; i++) {//这里的modelLoaders,是在创建MultiModelLoader的时候赋值的ModelLoader<Model, Data> modelLoader = modelLoaders.get(i);//在这里有进行了一次过滤,在更小的范围内查找,可以处理model的ModelLoader。(这次是在StringLoader中查找,可以处理URL的ModelLoader)if (modelLoader.handles(model)) {LoadData<Data> loadData = modelLoader.buildLoadData(model, width, height, options);if (loadData != null) {//这里把LoadData的值提取出来,sourceKey = loadData.sourceKey;//以本例子,这里的fetcher 是HttpUrlFetcher fetchers.add(loadData.fetcher);}}}return !fetchers.isEmpty() && sourceKey != null? new LoadData<>(sourceKey, new MultiFetcher<>(fetchers, exceptionListPool)): null;}
HttpUrlFetcher loadDataWithRedirects方法
这里我们回到之前的startNextLoad方法,通过该方法可以DataFetcher ,如果这里的Fetcher是HttpUrlFetcher,那么会很自然的执行下面的loadData方法,进而通过loadDataWithRedirects拿到网络请求的数据,在该方法内还包括对图片地址进行重定向的操作。这里如果数据获取成功就会通过 callback.onDataReady(result); 进行数据的回调。
@Overridepublic void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {long startTime = LogTime.getLogTime();try {InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());//获取数据成功后,这里进行了回调callback.onDataReady(result);} catch (IOException e) {if (Log.isLoggable(TAG, Log.DEBUG)) {Log.d(TAG, "Failed to load data for url", e);}//获取数据失败后,这里进行了回调callback.onLoadFailed(e);} finally {if (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));}}}private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {if (redirects >= MAXIMUM_REDIRECTS) {throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");} else {// Comparing the URLs using .equals performs additional network I/O and is generally broken.// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.try {if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {throw new HttpException("In re-direct loop");}} catch (URISyntaxException e) {// Do nothing, this is best effort.}}urlConnection = connectionFactory.build(url);for (Map.Entry<String, String> headerEntry : headers.entrySet()) {urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());}urlConnection.setConnectTimeout(timeout);urlConnection.setReadTimeout(timeout);urlConnection.setUseCaches(false);urlConnection.setDoInput(true);// Stop the urlConnection instance of HttpUrlConnection from following redirects so that// redirects will be handled by recursive calls to this method, loadDataWithRedirects.urlConnection.setInstanceFollowRedirects(false);// Connect explicitly to avoid errors in decoders if connection fails.urlConnection.connect();// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.stream = urlConnection.getInputStream();if (isCancelled) {return null;}final int statusCode = urlConnection.getResponseCode();if (isHttpOk(statusCode)) {//如果请求成功,返回数据流return getStreamForSuccessfulRequest(urlConnection);} else if (isHttpRedirect(statusCode)) {//拿到重定向地址,进行重定向。String redirectUrlString = urlConnection.getHeaderField("Location");if (TextUtils.isEmpty(redirectUrlString)) {throw new HttpException("Received empty or null redirect url");}URL redirectUrl = new URL(url, redirectUrlString);// Closing the stream specifically is required to avoid leaking ResponseBodys in addition// to disconnecting the url connection below. See #2352.cleanup();return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);} else if (statusCode == INVALID_STATUS_CODE) {throw new HttpException(statusCode);} else {throw new HttpException(urlConnection.getResponseMessage(), statusCode);}}
SourceGenerator onDataReadyInternal方法
如果通过网络成功拿到数据就会回调onDataReadyInternal
@Syntheticvoid onDataReadyInternal(LoadData<?> loadData, Object data) {DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {//如果该数据类型,有启用磁盘缓存,就把值付给dataToCache dataToCache = data;// We might be being called back on someone else's thread. Before doing anything, we should// reschedule to get back onto Glide's thread.//调用DecodeJob的reschedule,用线程池执行任务,实际上就是再次调用SourceGenerator的startNextcb.reschedule();} else {// 回调函数cb.onDataFetcherReady(loadData.sourceKey,data,loadData.fetcher,loadData.fetcher.getDataSource(),originalKey);}}
DecodeJob onDataFetcherReady
之后通过cb(FetcherReadyCallback)的onDataFetcherReady方法进一步进行下一步的传参回调。将拿到的data等进一步传给DecodeJob类中。后面执行decodeFromRetrievedData(),以及最终调用GlideTrace.endSection()的方法。
@Overridepublic void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {this.currentSourceKey = sourceKey;this.currentData = data;this.currentFetcher = fetcher;this.currentDataSource = dataSource;this.currentAttemptingKey = attemptedKey;if (Thread.currentThread() != currentThread) {runReason = RunReason.DECODE_DATA;callback.reschedule(this);//即使在这里取消了作业,仍需要对其进行计划,以便可以自行清理} else {GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");try {decodeFromRetrievedData();} finally {GlideTrace.endSection();}}}
DecodeJob decodeFromRetrievedData
再次我们首先可以通过log了解到key,data等情况,然后进行数据的解码,以及通知编码和发布,最终得到的Resource对象
private void decodeFromRetrievedData() {if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Retrieved data",startFetchTime,"data: "+ currentData+ ", cache key: "+ currentSourceKey+ ", fetcher: "+ currentFetcher);}Resource<R> resource = null;try {//从数据中解码得到资源resource = decodeFromData(currentFetcher, currentData, currentDataSource);} catch (GlideException e) {e.setLoggingDetails(currentAttemptingKey, currentDataSource);throwables.add(e);}if (resource != null) {// 通知编码和发布,最终得到的Resource<Bitmap>对象notifyEncodeAndRelease(resource, currentDataSource);} else {runGenerators();}}
DecodeJob decodeFromData
通过传入参数,然后一步一步实现进入解压方法。
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {try {if (data == null) {return null;}long startTime = LogTime.getLogTime();//设置解码开始时间,然后正式进行解码Resource<R> result = decodeFromFetcher(data, dataSource);if (Log.isLoggable(TAG, Log.VERBOSE)) {logWithTimeAndKey("Decoded result " + result, startTime);}return result;} finally {fetcher.cleanup();}}//这里的data 是一个泛型,本例中是 Stream 流,从http请求获取到的private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)throws GlideException {//获取一个LoadPath,它是根据数据类型(这里是stream),ResourceDecoder(资源解码),transcoder(资源转码)//从这些参数可以看出,最终把数据流转为Bitmap 或Drawable ,就是在LoadPath中进行的,这里是指DecodePath LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());return runLoadPath(data, dataSource, path);}private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)throws GlideException {Options options = getOptionsWithHardwareConfig(dataSource);DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);try {// ResourceType in DecodeCallback below is required for compilation to work with gradle.//执行LoadPath 的load ,进行解码,转换操作return path.load(rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));} finally {rewinder.cleanup();}}
LoadPath load()方法
在这进行一些异常的处理和判空的处理,最后解压的重头戏在decode方法里
public Resource<Transcode> load(DataRewinder<Data> rewinder,@NonNull Options options,int width,int height,DecodePath.DecodeCallback<ResourceType> decodeCallback)throws GlideException {List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());try {return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);} finally {listPool.release(throwables);}}private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,@NonNull Options options,int width,int height,DecodePath.DecodeCallback<ResourceType> decodeCallback,List<Throwable> exceptions)throws GlideException {Resource<Transcode> result = null;//noinspection ForLoopReplaceableByForEach to improve perffor (int i = 0, size = decodePaths.size(); i < size; i++) {DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);try {//重点在这里,调用LoadPath 的decode 解码result = path.decode(rewinder, width, height, options, decodeCallback);} catch (GlideException e) {exceptions.add(e);}if (result != null) {break;}}if (result == null) {throw new GlideException(failureMessage, new ArrayList<>(exceptions));}return result;}
DecodePath decode()方法
在这一步就进行了资源的解码,然后根据DataType和ResourceType的类型分发给不同的解码器Decoder,这里的Decoder实现在不同的类里,包括ByteBufferBitmapDecoder,GifDrawableResourc等等,以便针对不同的资源分别进行处理。其中在后续处理的时候会用到的downsampler主要是对流进行解码,旋转,压缩,圆角等处理。那么到此解码的大概流程就到这,再次以及得到了解析后的资源了,接下来就是要显示到指定的ImageView控件上。
public Resource<Transcode> decode(DataRewinder<DataType> rewinder,int width,int height,@NonNull Options options,DecodeCallback<ResourceType> callback)throws GlideException {Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);//进行获取Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);//进行回调return transcoder.transcode(transformed, options);}@NonNullprivate Resource<ResourceType> decodeResource(DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options)throws GlideException {List<Throwable> exceptions = Preconditions.checkNotNull(listPool.acquire());try {return decodeResourceWithList(rewinder, width, height, options, exceptions);} finally {listPool.release(exceptions);}}@NonNullprivate Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder,int width,int height,@NonNull Options options,List<Throwable> exceptions)throws GlideException {Resource<ResourceType> result = null;//noinspection ForLoopReplaceableByForEach to improve perffor (int i = 0, size = decoders.size(); i < size; i++) {ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);try {DataType data = rewinder.rewindAndGet();if (decoder.handles(data, options)) {data = rewinder.rewindAndGet();//根据DataType和ResourceType的类型分发给不同的解码器Decoderresult = decoder.decode(data, width, height, options);}// Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but// instead log and continue. See #2406 for an example.//一些解码器意外抛出。如果这样做,我们不应使整个加载路径失败,而是登录并继续。} catch (IOException | RuntimeException | OutOfMemoryError e) {if (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "Failed to decode data for " + decoder, e);}exceptions.add(e);}if (result != null) {break;}}
decode是一个ResourceDecoder接口(资源解码器),根据不同的DataType和ResourceType它会有不同的实现类,这里的实现类是ByteBufferBitmapDecoder
DecodePath notifyEncodeAndRelease()方法
回到之前的decodeFromRetrievedData(),此时我们以及拿到资源resource了,就下来就是通过notifyEncodeAndRelease(resource, currentDataSource);去进行一个图片的资源加载,将图片缓存进行转换加载。
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {if (resource instanceof Initializable) {((Initializable) resource).initialize();}Resource<R> result = resource;LockedResource<R> lockedResource = null;if (deferredEncodeManager.hasResourceToEncode()) {lockedResource = LockedResource.obtain(resource);result = lockedResource;}// 通知主线程回调,加载图片notifyComplete(result, dataSource);// 更新状态为编码stage = Stage.ENCODE;try {//是否可以将转换的图片缓存if (deferredEncodeManager.hasResourceToEncode()) {//磁盘缓存入口deferredEncodeManager.encode(diskCacheProvider, options);}} finally {if (lockedResource != null) {lockedResource.unlock();}}// Call onEncodeComplete outside the finally block so that it's not called if the encode process// throws.onEncodeComplete();}private void notifyComplete(Resource<R> resource, DataSource dataSource) {setNotifiedOrThrow();// 这个callback 就是 EngineJob对象,是在创建Decodejob的时候callback.onResourceReady(resource, dataSource);}
EngineJob onResourceReady()方法
在onResourceReady方法内进行资源接口的回调,进行缓存引用等等
@Overridepublic void onResourceReady(Resource<R> resource, DataSource dataSource) {synchronized (this) {this.resource = resource;this.dataSource = dataSource;}notifyCallbacksOfResult();}@Syntheticvoid notifyCallbacksOfResult() {//这个类是重点ResourceCallbacksAndExecutors copy;Key localKey;EngineResource<?> localResource;synchronized (this) {stateVerifier.throwIfRecycled();if (isCancelled) {// TODO: Seems like we might as well put this in the memory cache instead of just recycling// it since we've gotten this far...resource.recycle();release();return;} else if (cbs.isEmpty()) {throw new IllegalStateException("Received a resource without any callbacks to notify");} else if (hasResource) {throw new IllegalStateException("Already have resource");}engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);//下面的回调期间保留资源,因此我们不会在通知其中一个回调是否已同步释放它的中间对其进行回收。在这里锁定下获取它,这样,在我们调用回调之前在下面的下一个锁定部分之前执行的所有新添加的回调都无法回收资源。// Hold on to resource for duration of our callbacks below so we don't recycle it in the// middle of notifying if it synchronously released by one of the callbacks. Acquire it under// a lock here so that any newly added callback that executes before the next locked section// below can't recycle the resource before we call the callbacks.hasResource = true;//cbs 在类初始化的时候,就被赋值,。 //engineJob.addCallback(cb, callbackExecutor); 这个cb 参数是SingleRequest 的对象实现了接口ResourceCallbackcopy = cbs.copy();incrementPendingCallbacks(copy.size() + 1);localKey = key;localResource = engineResource;}//这里就是把解析后的图片,也就是即将要显示出来的图片,缓存到弱引用缓存中engineJobListener.onEngineJobComplete(this, localKey, localResource);for (final ResourceCallbackAndExecutor entry : copy) {//遍历每一个回调接口,entry.cb 就是SingleRequest 对象,执行接口ResourceCallbackentry.executor.execute(new CallResourceReady(entry.cb));}decrementPendingCallbacks();}
private class CallResourceReady implements Runnable {private final ResourceCallback cb;CallResourceReady(ResourceCallback cb) {this.cb = cb;}@Overridepublic void run() {// Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock// (b/136032534).synchronized (cb.getLock()) {synchronized (EngineJob.this) {if (cbs.contains(cb)) {// Acquire for this particular callback.engineResource.acquire();//执行回调callCallbackOnResourceReady(cb);removeCallback(cb);}decrementPendingCallbacks();}}}}@Synthetic@GuardedBy("this")void callCallbackOnResourceReady(ResourceCallback cb) {try {// This is overly broad, some Glide code is actually called here, but it's much// simpler to encapsulate here than to do so at the actual call point in the// Request implementation.cb.onResourceReady(engineResource, dataSource);} catch (Throwable t) {throw new CallbackException(t);}}
最后会进一步走到SingleRequest里的target.onResourceReady(result, animation);语句,然后在ImageViewTarget类里对资源进行设置
@Overridepublic void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {if (transition == null || !transition.transition(resource, this)) {setResourceInternal(resource);} else {maybeUpdateAnimatable(resource);}}private void setResourceInternal(@Nullable Z resource) {// Order matters here. Set the resource first to make sure that the Drawable has a valid and// non-null Callback before starting it.//这是一个抽象函数,执行不同的实现,本例值得是BitmapImageViewTargetsetResource(resource);maybeUpdateAnimatable(resource);}
不论是缩略图,Bitmap,Drawable都会进行覆写这个方法,然后在自己的绘制逻辑下将图片显示出来。
@Overrideprotected void setResource(Bitmap resource) {view.setImageBitmap(resource);}
到此整个文章的对于Glide的一个源码讲解流程就结束了,如果希望更深的了解这一层次的东西,你可能需要更深层的去了解和阅读。
参考文章
深入分析Glide源码
Glide 源码分析解读-基于最新版Glide 4.9.0
Androd Glide核心逻辑解析