前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Glide4源码解析系列] — 1.Glide初始化

[Glide4源码解析系列] — 1.Glide初始化

作者头像
开发的猫
发布2020-04-01 17:13:02
8750
发布2020-04-01 17:13:02
举报
文章被收录于专栏:猫哥的专栏猫哥的专栏

Glide


Glide4源码解析系列

[Glide4源码解析系列]--1.Glide初始化

[Glide4源码解析系列]--2.Glide数据模型转换与数据抓取

[Glide4源码解析系列]--3.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中进行指定:

<meta-data android:name="com.test.GlideConfiguration"
           android:value="GlideModule"/>

而在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) {
    }
}



本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
  • 二、Glide.with发生了什么?
    • 1. Glide单例的加载
      • 2. GlideModule配置加载
        • 3. GlideBuilder构建Glide单例
          • 4. 构建Glide,配置数据转换器/解码器/转码器/编码器
            • 5. 生命周期管理
            • 三、 Glide与GlideApp
            相关产品与服务
            图片处理
            图片处理(Image Processing,IP)是由腾讯云数据万象提供的丰富的图片处理服务,广泛应用于腾讯内部各产品。支持对腾讯云对象存储 COS 或第三方源的图片进行处理,提供基础处理能力(图片裁剪、转格式、缩放、打水印等)、图片瘦身能力(Guetzli 压缩、AVIF 转码压缩)、盲水印版权保护能力,同时支持先进的图像 AI 功能(图像增强、图像标签、图像评分、图像修复、商品抠图等),满足多种业务场景下的图片处理需求。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档