Glide系列(一) — Glide 框架结构浅析

一、概述

1.1 背景

Glide 是一个图片加载库,跟它同类型的库还有 Picasso、Fresco、Universal-Image-Loader 等。基于 Glide 优秀的缓存管理策略和生命周期关联的特点,目前市面上对 Glide 的使用非常广,因此我们有必要深入研究下 Glide 相关的实现原理,便于更好的使用它。

目前 Glide 版本已经更新到 4.11.0,但本系列我们仍采用 Glide3.7.0 版本进行分析,原因有两个:

  • 3.7.0 版本代码相对比较简单。
  • 与 3.7.0 版本相比,4.0+ 版本之后代码改动较大。

所以本着先易后难的原则,我们使用 Glide 3.7.0 来进行分析,对 Glide 整体有一个全面了解之后,再去分析 4.0+ 版本。


Glide 版本: Glide 3.7.0
Github 地址: https://github.com/bumptech/glide/tree/v3.7.0
Gradle 依赖: implementation 'com.github.bumptech.glide:glide:3.7.0'

1.2 Glide 框架的优点

  1. 加载类型多样化:Glide 支持 Gif、WebP、Jpeg、Png 等格式的图片。
  2. 生命周期的绑定:图片请求与页面生命周期绑定,避免内存泄漏。
  3. 使用简单(链式调用),且提供丰富的 Api 功能 (如: 图片裁剪等功能)。
  4. 高效的缓存策略:
    1. 支持多种缓存策略 (Memory 和 Disk 图片缓存)。
    2. 根据 ImageView 的大小来加载相应大小的图片尺寸。
    3. 内存开销小,默认使用 RGB_565 格式 (3.x 版本)。
    4. 复用对象 (享元模式),降低内存的抖动。
    5. 通过 Lru 算法来管理内存缓存和磁盘缓存,保证资源使用的可控性。

1.3 系列文章

  1. Glide系列(一) — Glide 框架结构浅析
  2. Glide系列(二) — DataLoadProvider 及与 Encoder&Decoder 的关系
  3. Glide系列(三) — LoadProvider、ModelLoader、DataFetcher 和 ResourceTranscoder 关系
  4. Glide系列(四) — Glide 缓存流程分析
  5. Glide系列(五) — Request 构建流程分析
  6. DiskLruCache 源码分析

二、Glide 框架整体结构设计

Glide 框架整体结构如下图所示。
在这里插入图片描述

Glide 框架结构如上图所示:

  1. Glide 框架主要分为两大流程:
    1.1 图片请求的构建流程
    1.2 图片缓存的获取流程
  2. 图片请求构建流程内又分为四个模块:
    2.1 用户态的请求模块: 用户使用 Glide 进行链式调用的时候会生成一个用户态的 Request。
    2.2 真实的请求模块: 由于步骤2.1中会根据场景构建多种Request,因此在发起图片请求时需要进行收口,因此会使用用户态的 Request 构建一个真正的 GenericRequest 请求。
    2.3 Request 生命周期管理模块: Glide 的一大特点是会将图片的请求和页面生命周期进行绑定,避免出现内存泄漏的风险,因此会有一个 Request 生命周期管理模块。
    2.4 Registry中心模块: 由于 Glide 支持加载多种类型的数据,因此在注册中心会预先注册所支持类型处理类的信息。
  3. 图片缓存一共分为3大层,5小层:
    3.1 内存缓存(2小层): 弱引用缓存、LruCache。
    3.2 本地缓存(2小层): 本地 ResultCache 缓存、本地 SourceCache 缓存。
    3.3 Source 数据源(1小层): 网络获取、本地 AssetPath 获取、其它本地图片。

小结:

看到这里,只要知道以下三点就可以了。

  1. 知道 Glide 分为两大流程 (请求创建流程、缓存获取流程) 就可以了。
  2. 知道 Glide 缓存部分分为3大层,5小层就可以了。
  3. 可以将 Glide 的两大流程与网络请求进行类比。网络请求也分为两个流程:网络请求的构建流程、数据的接收和响应流程 (Glide的缓存获取流程)。
  4. 这里我们对五层缓存进行定义,方便后面引用说明:
    1. 第一层:内存缓存 -> 弱应用缓存(ActiveResource)。
    2. 第二层:内存缓存 -> LruCache
    3. 第三层:磁盘缓存 -> ResultCache (经过transform的图)
    4. 第四层:磁盘缓存 -> SourceCache (从DataFetcher 直接拉取到的数据)
    5. 第五层:数据源 -> Source

三、Glide中涉及的几个基本概念

在研究 Glide 之前,需要对下面几个类有一个基本概念。

Glide中涉及的几个基本概念:

  • Target、ViewTarget 是什么?
  • Resource 是什么?
  • Encoder、ResourceEncoder 是什么?
  • ResourceDecoder 是什么?
  • Transformation 是什么?
  • ResourceTranscoder 是什么?
  • DataFetcher、ModelLoader 是什么?
  • DataLoadProvider、LoadProvider 是什么?
  • DataLoadProvider、ModelLoader、DataFetcher 之间的关系?

3.1 Target、ViewTarget 是什么?

  • Target、ViewTarget 的作用类似于网络请求中的 Callback,主要是用于处理图片已经返回后的逻辑。主要的回调方法如下图所示。
  • Target 与 ViewTarget 的主要区别在于 ViewTarget 中包含有一个设置图片的 View。
  • Target 实现了 LifecycleListener,方便在处理生命周期时调用对应的方法。
public interface Target<R> extends LifecycleListener {
	// 开始请求
    void onLoadStarted(Drawable placeholder);
	// 请求失败
    void onLoadFailed(Exception e, Drawable errorDrawable);
	// 请求成功
    void onResourceReady(R resource, GlideAnimation<? super R> glideAnimation);
}

3.2 Resource 是什么?

  • Resource 的作用类似于网络请求返回时统一的数据封装 Result,目的是便于数据的传输。
  • 同时针对 Resource 包含的不同数据类型,获取相应的编解码器等。
// 网络接口数据封装
class Result<T> {
	T data;
}

// 用于包装从缓存中获取的数据(图片)
public interface Resource<Z> {
    Z get();
}
// 具体的使用场景
public class BitmapResource implements Resource<Bitmap> {
    private final Bitmap bitmap;
    @Override
    public Bitmap get() {
        return bitmap;
    }
}

3.3 Encoder、ResourceEncoder 是什么?

  • Encoder、ResourceEncoder 用于执行将图片信息保存成文件进行缓存。encode() 方法会传入要保存的数据和OutputStream 两个参数,所以 Encoder 主要是执行。
  • Encoder.encode() 方法接收任意类型的输入,且将数据写入到 OutputStream 中。
  • ResourceEncoder.encode() 方法接收 Resource 类型的数据。
public interface Encoder<T> {
	// 接收任意类型的输入,并将数据写入到 OutputStream 中。
    boolean encode(T data, OutputStream os);
}

public interface ResourceEncoder<T> extends Encoder<Resource<T>> {
	// 接收Resource<T>类型的数据,并将数据写入到 OutputStream 中。
    boolean encode(Resource<T> data, OutputStream os);
}
// 具体的实现类:将Bitmap保存成文件。
public class BitmapEncoder implements ResourceEncoder<Bitmap> {
    private static final int DEFAULT_COMPRESSION_QUALITY = 90;
    private Bitmap.CompressFormat compressFormat;
    private int quality;

    @Override
    public boolean encode(Resource<Bitmap> resource, OutputStream os) {
        final Bitmap bitmap = resource.get();
        Bitmap.CompressFormat format = getFormat(bitmap);
        // 压缩
        bitmap.compress(format, quality, os);
        return true;
    }
}

3.4 ResourceDecoder 是什么?

  • ResourceDecoder ResourceEncoder 类似,主要用于将图片缓存(文件)解析成要使用的数据格式(如Bitmap)。
  • ResourceDecoder.decode() 方法接收任意类型(只要能拿到缓存数据即可:如InputStream、File等),返回的类型需要使用Resource包装,方便传输。
public interface ResourceDecoder<T, Z> {
	// 接收任意类型(只要能拿到缓存数据即可:如InputStream、File等),返回的类型需要使用Resource包装,方便传输。
	Resource<Z> decode(T source, int width, int height) throws IOException;
}

public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {
    private final Downsampler downsampler;

	// 具体场景:接收File文件,然后转换成Bitmap格式后进行包装,最终返回Resource<Bitmap>数据类型。
    @Override
    public Resource<Bitmap> decode(InputStream source, int width, int height) {
        Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
        return BitmapResource.obtain(bitmap, bitmapPool);
    }
}

3.5 Transformation 是什么?

  • Transformation 是一个数据转换器,转换前后数据的类型不变。它的主要作用是对返回的数据进行一些加工处理,如倒圆角等,它与 ResourceTranscoder 的作用是不同的。
public interface Transformation<T> {
	// 转换前后数据的类型不变。
    Resource<T> transform(Resource<T> resource, int outWidth, int outHeight);
}

3.6 ResourceTranscoder 是什么?

  • ResourceTranscoder 是一个转码器,会改变数据类型 (将 Resource 的参数类型转换为 Resource 类型并返回)。与 Transformation 不同。
  • 所以 TransformationResourceTranscoder 就组成了 数据同类型转换不同类型转换 的功能。
public interface ResourceTranscoder<Z, R> {
	// 将 Resource<Z> 的参数类型转换为 Resource<R> 类型。
    Resource<R> transcode(Resource<Z> toTranscode);
}

// 具体示例:将Bitmap转换为GlideBitmapDrawable类型。
public class GlideBitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, GlideBitmapDrawable> {
	
    @Override
    public Resource<GlideBitmapDrawable> transcode(Resource<Bitmap> toTranscode) {
        GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
        return new GlideBitmapDrawableResource(drawable, bitmapPool);
    }
}

3.7 DataFetcher、ModelLoader 是什么?

  • DataFetcher 是具体加载数据的类 (如: 从网络上加载图片数据)。
  • ModelLoader 的功能主要是预处理传入 DataFetcher 的参数,以及返回对应的 DataFetcher 的类型 (代码如下所示)。
public interface DataFetcher<T> {
	// 加载数据
    T loadData(Priority priority) throws Exception;
}
// 具体示例:
public class HttpUrlFetcher implements DataFetcher<InputStream> {
    @Override
    public InputStream loadData(Priority priority) throws Exception {
        return loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
    }
}


public interface ModelLoader<T, Y> {
    DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}

public abstract class UriLoader<T> implements ModelLoader<Uri, T> {
    private final ModelLoader<GlideUrl, T> urlLoader;

    @Override
    public final DataFetcher<T> getResourceFetcher(Uri model, int width, int height) {
        final String scheme = model.getScheme();
        DataFetcher<T> result = null;
        // 加载本地路径数据的DataFetcher。
        if (isLocalUri(scheme)) {
            result = getLocalUriFetcher(context, model);
        } else if (urlLoader != null && ("http".equals(scheme) || "https".equals(scheme))) {
        	// 加载非本地路径数据的DataFetcher。
            result = urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height);
        }

        return result;
    }
    
    protected abstract DataFetcher<T> getLocalUriFetcher(Context context, Uri uri);
}

3.8 DataLoadProvider、LoadProvider 是什么?

  • DataLoadProvider 是一个数据的提供者,主要是负责对数据进行处理 (如将Bitmap保存为缓存文件,将缓存文件转换为Bitmap),因此 DataLoadProvider 主要是对外提供编解码的对象 (Encoder、Decoder)。
  • LoadProvider 继承自 DataLoadProvider,所以它具备将文件的编解码功能,加载文件的能力,对数据进行类型转换的能力。
  • DataLoadProvider 中两个泛型参数<T, Z>的含义如下面代码所示 (需要理解:看下面代码)。
  • 要将一个图片缓存文件展示到屏幕上的步骤:
    1. 通过 LoadProvider 获取到 ModelLoader,继而找到加载文件的 DataFetcher,此时返回的数据要么是流,要么是fd。
    2. 通过 LoadProvider 获取到 ResourceDecoder 解码器对 DataFetcher 返回的数据进行解码生成T,并返回Resource。
    3. 通过 LoadProvider 获取到 ResourceTranscoder 转码器对 Resource进行操作,返回Resource后设置给控件。

代码中的注释说明很重要。

/*
 * @param <T> 对输入类型为T的数据进行解码。
 * @param <Z> 解码后返回的类型为Resource<Z>。
 */
public interface DataLoadProvider<T, Z> {
	// Cache缓存的解码器只支持接收File类型,返回Resource<Z>类型的数据。
	// Cache缓存:指第3、4层缓存,即ResultCache、SourceCache缓存。
	// 为什么Cache解码器只支持传入File进行解码? 因为这两层的缓存是Glide自己生成的,所以确定是File类型
    ResourceDecoder<File, Z> getCacheDecoder();
    // Source(指第5层缓存,即网络数据,asset目录数据等)的解码器支持接收T类型,返回Resource<Z>类型的数据。
    ResourceDecoder<T, Z> getSourceDecoder();
    // 接收T类型的数据进行编码(转成文件)。
    // 将从第5层缓存(数据源)获取到的数据缓存到第4层。
    Encoder<T> getSourceEncoder();
    // 接收Resource<Z>类型的数据进行编码(转成文件)。
    // 对经过transform()之后的数据进行编码(保存成文件)。即将第四层SourceCache数据缓存到第三层ResultCache。
    ResourceEncoder<Z> getEncoder();
}

/**
 * 泛型参数含义:
 * A、T:将A类型的数据通过ModelLoader加载成T类型数据,如将File类型的数据加载成InputStream类型数据。
 * Z、R:将Z类型的数据通过转码器转换成R类型数据,如将Bitmap类型的数据转成Drawable类型数据。
 */
public interface LoadProvider<A, T, Z, R> extends DataLoadProvider<T, Z> {
    ModelLoader<A, T> getModelLoader();
    ResourceTranscoder<Z, R> getTranscoder();
}

DataLoadProvider 中几种 编/解码器 的使用场景如下图所示:
在这里插入图片描述

3.9 DataLoadProvider、ModelLoader、DataFetcher 之间的关系?

介绍完了上面部分后,我们来看下他们之间的关系,如下图所示:

DataLoadProvider、ModelLoader、DataFetcher 之间的关系
在这里插入图片描述

小结: 看到这里,需要了解如下信息。

  1. DataLoadProvider 内持有处理图片相关的编/解码器。
  2. LoadProvider 是一个聚合类,继承自 DataLoadProvider 接口,同时聚合了 ModelLoader 和 ResourceTranscoder。因此具备了数据加载能力,编解码能力,数据转码能力。
  3. ModelLoader 会根据提供的加载类型返回一个相应的 DataFetcher。

四、总结

  1. 了解本文中涉及的 Resource、Encoder、ResourceDecoder、ResourceTranscoder 等的基本概念及其作用。
  2. 了解 DataLoadProvider、ModelLoader、DataFetcher 是什么,它们之间的关系?
  3. 了解Glide的缓存的分层及其类型。
  • 2
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值