Glide4 源码解析

您所在的位置:网站首页 gilde源码 Glide4 源码解析

Glide4 源码解析

2023-08-15 10:08| 来源: 网络整理| 查看: 265

Glide 一、前言

在众多的图片加载框架中,Glide是Google推荐的,并在自家的项目中大量使用的一个非常强大的框架,专注于平滑滚动,并且还提供Gif,本地Vedio首帧的解码和显示。Glide提供了非常便捷的链式调用接口,以及丰富的拓展和自定义功能,开发者可以非常简单地对框架进行配置和图片再加工。

如今Gilde已经更新到4.x,了解其源码对更好的使用Glide,以及学习相关的图片处理技术,学习更优雅的编码会有很大的帮助。

不得不说,Glide整个框架的极其复杂的,特别是在对资源的转换和解码过程中,涉及了许多的嵌套循环,同时也使用了大量的工厂模式用于生产转换模块,编码模块,解码模块等,笔者在阅读过程中,多次迷失在茫茫的代码流中。

为此,萌生了将对Glide的理解记录成文的想法,借以理清思路,也希望这一系列的文章可以帮助到无论是了解,还是准备阅读Glide源码的你,稍微理清一些思路。如有不对的地方,欢迎指正~

那么接下来,我们就先看看Glide是如何进行框架初始化的。

注意:本文源码版本为v4.6.1,不同版本可能存在一些差异!

二、Glide.with发生了什么? 1. Glide单例的加载

使用过Glide的都知道,调用Glide加载一张图片时,第一句代码便是Glide.with(this),这里肯定就是Glide的入口了,通过这句代码,Glide开始了“漫漫的”初始化之路。

Glide重载了多个with的方法,分别用于不同的情境下使用,我们看其中最常用的在Activity中调用的方法,即

首先,跟进getRetriever(activity)

这里首先检查了context是否为空,如果为null,抛出异常。

我们重点来看Glide.get(context)

这里是一个典型的双检锁单例模式。

继续跟进checkAndInitialzeGlide(context)

注意这里,在最后注入了一个GlideBuilder,这个就是Glide的建造器,用于构建Glide的一些参数和配置。

最后,来到真正初始化Glide的方法(代码去除了一些Log打印)。

留意最后将初始化得到的glide赋值给了Glide.glide的单例。

接下里就来看看在这初始化方法中,Glide都加载了哪些配置。

2. GlideModule配置加载

在使用Glide的时候,我们都会有一些想要设置的系统级配置,如设置缓存的存储位置,缓存区的大小,网络加载模块等等,那么我们通常就是使用GldieModule进行配置。在Glide3.x中,我们首先会定义一个继承于GlideModule的类,然后在项目的AndroidMenifest.xml中进行指定:

而在Glide4中,提供另外一个配置的模式,那就是注解,并且不再继承GlideModule,而是继承AppGlideModule和LibraryGlideModule,分别对应Application和Library,使用@GlideModule注解进行标记。而Glide3.x中的配置方式已经建议放弃使用。

@GlideModule public class GlideConfiguration extends AppGlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { //设置缓存到外部存储器 builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context)); } }

那么,Glide是如何对GlideModule的配置进行初始化的呢?

第二行代码中,getAnnotationGeneratedGlideModules()会获取Glide注解自动生产的一个Glide的Module配置器。如下:

其中‘com.bumptech.glide.GeneratedAppGlideModuleImpl’是在编译时由Glide生成的一个类,主要用于过滤不必要的GlideModule,以及提供一个请求检索器工厂,这个后面会讲到。

接下生成一个Manifest解析器ManifestParser,用于获取配置的GlideModule,并存放在manifestModules中。然后是一个判断

if (annotationGeneratedModule != null && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) { ...... }

如果条件成立,即编译时自动生成的类中,包含了需要排除的GlideModule,逐个将其移除。

接着以上代码,Glide将逐个调用剩下的GlideModule,并回调applyOptions和registerComponents接口,这时,用户配置的GlideModule就会被调用,同时用户设置的参数也就被配置到Glide中。

在以上代码中,发现一句代码,在回调registerComponents前,首先构建了glide的实例。

这是一句非常重要的代码,整个Glide框架最重要的初始化内容都在其中实现。

Glide glide = builder.build(applicationContext); 3. GlideBuilder构建Glide单例

跳转到GlideBuilder中,看build方法做了哪些事情。代码并不复杂,直接看代码中的注释。

通过以上一系列工具的新建,Glide建立了资源请求线程池,本地缓存加载线程池,动画线程池,内存缓存器,磁盘缓存工具等等,接着构造了Engine数据加载引擎,最后再将Engine注入Glide,构建Glide。

其中还建立了一个请求器索引器,用于索引RequestManger,后面我们再详细讲。

我们进入最后, 构建Glide。

4. 构建Glide,配置数据转换器/解码器/转码器/编码器

回到Glide中,看看Glide的构造函数,这是一个长得变态的构造函数(有200行),但是不必被它吓倒(好吧,其实第一次看到这里,我是被吓倒了,直接略过去了,限于文章篇幅,这里只截取了部分源码,仔细的话可以直接看源码),仔细分析一下,其实整个构造过程并没那么复杂。

其中最重要的是步骤3和步骤4,分别为Glide初始化了模型转换加载器,解码器,转码器,编码器,并将对各种类型进行一一注册,将其列成表格如下:

模型转换器 转换器 功能 ResourceLoader.StreamFactory 将Android资源ID转换为Uri,在加载成为InputStream ResourceLoader.UriFactory 将资源ID转换为Uri ResourceLoader.FileDescriptorFactory 将资源ID转化为ParcelFileDescriptor ResourceLoader.AssetFileDescriptorFactory 将资源ID转化为AssetFileDescriptor UnitModelLoader.Factory 不做任何转换,返回源数据 ByteBufferFileLoader.Factory 将File转换为ByteBuffer FileLoader.StreamFactory 将File转换为InputStream FileLoader.FileDescriptorFactory 将File转化为ParcelFileDescriptor DataUrlLoader.StreamFactory 将Url转化为InputStream StringLoader.StreamFactory 将String转换为InputStream StringLoader.AssetFileDescriptorFactory 将String转换为AssetFileDescriptor HttpUriLoader.Factory 将http/https Uri转换为InputStream UriLoader.StreamFactory 将Uri转换为InputStream UriLoader.FileDescriptorFactory 将Uri转换为ParcelFileDescriptor UriLoader.AssetFileDescriptorFactory 将Uri转换为AssetFileDescriptor UrlUriLoader.StreamFactory 将将http/https的Uri转换为InputStream UrlLoader.StreamFactory 将Url转换为InputStream HttpGlideUrlLoader.Factory 将HttpGlide转换为InputStream ...... ...... 解码器 解码器 功能 ByteBufferGifDecoder 将ByteBuffer解码为GifDrawable ByteBufferBitmapDecoder 将ByteBuffer解码为Bitmap ResourceDrawableDecoder 将资源Uri解码为Drawable ResourceBitmapDecoder 将资源ID解码为Bitmap BitmapDrawableDecoder 将数据解码为BitmapDrawable StreamBitmapDecoder 将InputStreams解码为Bitmap StreamGifDecoder 将InputStream数据转换为BtyeBuffer,再解码为GifDrawable GifFrameResourceDecoder 解码gif帧 FileDecoder 包装File成为FileResource UnitDrawableDecoder 将Drawable包装为DrawableResource UnitBitmapDecoder 包装Bitmap成为BitmapResource VideoDecoder 将本地视频文件解码为Bitmap 转码器 转码器 功能 BitmapDrawableTranscoder 将Bitmap转码为BitmapDrawable BitmapBytesTranscoder 将Bitmap转码为Byte arrays DrawableBytesTranscoder 将BitmapDrawable转码为Byte arrays GifDrawableBytesTranscoder 将GifDrawable转码为Byte arrays 编码器 编码器 功能 ByteBufferEncoder 将Byte数据缓存为File StreamEncoder InputStream缓存为File BitmapEncoder 将Bitmap数据缓存为File BitmapDrawableEncoder 将BitmapDrawable数据缓存为File GifDrawableEncoder 将GifDrawable数据缓存为File 模型转换注册表(实在太多,只列出了部分) 源数据 转换数据 转换器 Integer.class InputStream.class ResourceLoader.StreamFactory Integer.class ParcelFileDescriptor.class ResourceLoader.FileDescriptorFactory ...... ...... ...... String.class InputStream.class DataUrlLoader.StreamFactory String.class InputStream.class StringLoader.StreamFactory ...... ...... ...... Uri.class InputStream.class DataUrlLoader.StreamFactory Uri.class InputStream.class HttpUriLoader.Factory Uri.class InputStream.class UriLoader.StreamFactory URL.class InputStream.class UrlLoader.StreamFactory ...... ...... ......

以上模型转换注册表非常重要,在Glide进入解码流程时,将会遍历这里注册的所有可能转换的情形,尝试进行数据转换。

这里只列出部分情形,其它还包括File/Bitmap/Drawable/Byte等等几乎涵括了日常使用的情况。

Glide的加载流程可以概括为以下流程:

model(数据源)-->data(转换数据)-->decode(解码)-->transformed(缩放)-->transcoded(转码)-->encoded(编码保存到本地)

其中,transformed为对解码得到的图片数据进行缩放,如FitCenter、CropCenter等。

到这里,Glide单例就构建完成了,让我们返回到Glide#with中

在构建好Glide后,通过getRequestManagerRetriever()将会得到一个RequestManagerRetriever,即RequestManager的检索器,RequestManagerRetriever#get()将为每个请求页面创建一个RequestManager。

还记得GlideBuilder#build提到的一句代码吗?

RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory);

没错,这里获取的就是它。这里就必须要讲到Glide数据请求的生命周期了。

我们都知道Glide会根据页面的生命周期来自动的开启和结束数据的请求,那么Glide是怎么做到的呢?

5. 生命周期管理

我们进入RequestManagerRetriever#get(Activity)方法中。

首先,判断是否为后台线程,如果是,则使用ApplicationContext重新获取。 重点来看else代码块。先断言请求的页面是否已经销毁。否则获取当前页面的FragmentManager,并传给fragmentGet方法。

在fragmentGet中首先通过getRequestManagerFragment()来获取一个命名为FRAGMENT_TAG的fragment,如不存在,则新建一个RequestManagerFragment,并添加到当前页面中。

这里我们就可以猜到了,Glide是通过在页面中添加一个Fragment来动态监听页面的创建和销毁,从而达到依赖页面生命周期,动态管理请求的目的。

在RequestManagerFragment构造函数中,注入了一个生命周期监听器ActivityFragmentLifecycle,并在Fragment各个生命周期回调中,调用了对应的方法。

而ActivityFragmentLifecycle也紧接着会调用lifecycleListener监听器,而这个监听器其实就是RequestManger。如下:

最后,RequestManagerRetriever#fragmentGet,判断这个Fragment的RequestManager是否存在,否则创建一个RequestManager,并将生命周期注入,同时RquestManager构建时,将会通过addListener注入生命周期回调(具体可以查看RequestManger构造函数)。

最后,Glide#with终将得到一个RequestManager。

至此,Glide的加载过程就解析完毕了。总结一下整个流程:

通过AndroidManifest和@GlideModule注解获取用户自定义配置GlideModule,并调用其对应的方法 通过GlideBuilder构建Glide: 1.新建线程池 2.新建图片缓存池和缓存池 3.新建内存缓存管理器 4.新建默认本地缓存管理器 5.新建请求引擎Engine 6.新建RequestManger检索器 7.新建Glide Glide构造方法中,新建模型转换器,解码器,转码器,编码器,以及生成Glide上下文GlideContext 通过RequestManager检索器,建立生命周期监听,并建立一个RequestManager 完成! 三、 Glide与GlideApp

如果在项目中已经使用了Glide3.x,并且想要升级到Glide4.x,那么你会发现,原来使用链式调用进行参数配置的方法已经被修改了,同一个封装到了RequesOptions中,如下:

RequestOptions options = new RequestOptions() .centerCrop() .placeholder(R.mipmap.ic_launcher_round) .error(R.mipmap.ic_launcher) .priority(Priority.HIGH) .diskCacheStrategy(DiskCacheStrategy.NONE); Glide.with(this) .load(ImageConfig.URL_GIF) .apply(options) .into(iv);

这样的话升级后将导致大量的修改,当然你也可以自己封装一下,但是Glide已经为我们做好了兼容方案。

还记得初始化是通过@GlideModule注解来注册自定义配置吗?只要在项目中定义这么一个配置,那么Glide将会自动帮我们生成一个GlideApp模块,封装了Glide3.x中的调用方式。

public class GlideConfiguration extends AppGlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { } }

调用如下,还是原来的配方,还是熟悉的味道~

GlideApp.with(this) .load(ImageConfig.URL_WEBP) .sizeMultiplier(0.5f) .centerCrop() .diskCacheStrategy(DiskCacheStrategy.ALL) .error(R.mipmap.ic_launcher) .into(iv);

如果你还觉得不爽,那么你甚至可以把GlideApp直接修改为Glide,实现几乎“无缝对接”。当然,你还是要修改引用路径的。

@GlideModule(glideName="Glide") public class GlideConfiguration extends AppGlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { } }

以上,就是Glide4初始化的源码解析了,其实还有许多的知识点可以继续深入,如AndroidManifest解析器,注解的使用(可参考我的另一文Android编译时注解,和重复代码Say No!)等等,有兴趣的话可以把源码下下来看看。

接下来将推出Glide4加载资源过程的源码分析,敬请期待~



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3