Hiten's Blog.

Glide4.8源码拆解(三)Registry和数据转换流程

字数统计: 3.3k阅读时长: 15 min
2019/01/09 Share

前言

Registry是Glide中非常重要的知识,可以把它理解成连结各个核心功能模块的集中营或者挂载中心,这一章节就来分解它是如何建立和运作的:

本章要讨论的内容:

  • Registry的基本构成;
  • 各个模块的功能和介绍;
  • 数据的转换流程;

从Registry开始

Registry是一个组件管理类,它的主要用途是扩展和替换Glide组件,这些组件包括加载,编码,解码等逻辑;Registry内部支持的模块类型如下:

Registry.java

1
2
3
4
5
6
7
private final ModelLoaderRegistry modelLoaderRegistry;
private final EncoderRegistry encoderRegistry;
private final ResourceDecoderRegistry decoderRegistry;
private final ResourceEncoderRegistry resourceEncoderRegistry;
private final DataRewinderRegistry dataRewinderRegistry;
private final TranscoderRegistry transcoderRegistry;//Resource转换模块注册
private final ImageHeaderParserRegistry imageHeaderParserRegistry;//文件头解析模块注册

Registry并不是承当所有模块的注册工作,而是把各个模块分配的不同的Registry当中;
主要模块的功能:

  • ModelLoaderRegistry ://数据加载模块注册
  • EncoderRegistry://所有对数据进行编码模块的注册
  • ResourceDecoderRegistry://处理过的解码模块注册
  • ResourceEncoderRegistry://处理过的编码模块注册
  • DataRewinderRegistry : //数据流重置起点模块注册
  • TranscoderRegistry: // Resource进行转换模块注册
  • ImageHeaderParserRegistry //图片头解析模块注册

Glide自身充当对外调用的门户,Registry提供了一下入口方法来实现各个模块的注册和调用;主要方法如下:

注册相关方法:

  • append() //尾步追加
  • prepend() //头部插入
  • register() //注册,相当于append()
  • replace() //替换掉相同条件的所有模块

操作相关的方法:

  • getLoadPath()//获取加载路径
  • getDecodePaths() //获取解析路径
  • getRegisteredResourceClasses()//获取所有匹配的ResourceClasses;
  • isResourceEncoderAvailable()//ResourceEncoder是否可用;
  • getResultEncoder()//获取Encoder;
  • getRewinder()//获取Rewinder;
  • getModelLoaders()//获取ModelLoader;

总结:Registry通过内部Registry分别管理不同类型的组件,Registry提供统一的入口方法来实现注册和获取;

下面对各个模块基本介绍:

模块简要分析

ModelLoader

ModelLoader是通过ModelLoaderRegistry进行管理,ModelLoader需要接受两个泛型类型<Model,Data>ModelLoader本身是一个工厂接口,主要工作是将复杂数据模型转通过DataFetcher转换成需要的Data,LoadData是ModelLoader的内部类,是对DataFetcher和Key的封装实体,ModelLoader的创建用ModelLoaderFactory,一个基本的ModelLoader创建应该是这个样子的:
参考HttpGlideUrlLoader

第一步:自定义类实现自ModelLoader,重写BuildLoadData()方法和handles()方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

@Nullable private final ModelCache<GlideUrl, GlideUrl> modelCache;

public HttpGlideUrlLoader() {
this(null);
}

public HttpGlideUrlLoader(@Nullable ModelCache<GlideUrl, GlideUrl> modelCache) {
this.modelCache = modelCache;
}

@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
@NonNull Options options) {
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}

@Override
public boolean handles(@NonNull GlideUrl model) {
return true;
}
}

buildLoadData()需要创建LoadData,需要传入Key和DataFetcher,handles()返回值代表是否接受当前model类型的,true代表接受,所以一般都是true;

第二步:自定义Fetcher实现DataFecher,重写loadData()、cleanup()、cancel()、getDataClass()、getDataSource()方法;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class HttpUrlFetcher implements DataFetcher<InputStream> {
public 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));
}
}
}
//清理工作
@Override
public void cleanup() {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {

}
}
if (urlConnection != null) {
urlConnection.disconnect();
}
urlConnection = null;
}
//取消请求
@Override
public void cancel() {
isCancelled = true;
}
//返回Data类型
@NonNull
@Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
//返回DataSource
@NonNull
@Override
public DataSource getDataSource() {
return DataSource.REMOTE;
}
}

第三步:创建Factory类,重写build()、teardown()方法,在build()方法中返回真正的MolderLoader对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<>(500);

@NonNull
@Override
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new HttpGlideUrlLoader(modelCache);//创建ModelLoader
}

@Override
public void teardown() {
// Do nothing.
}
}

接下来,就可以在Registry中注册ModelLoader了;在第一章我们简单说过模块的配置可以用Annotation和Manifest两种类型,在registerComponents()方法中,可以拿到Registry,这样就可以调用registry的注册相关方法;

1
2
3
4
@Override
public void registerComponents(Context context, Registry registry) {
registry.replace(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory(context));
}

最后,说一下泛型<Model,Data>接受的范围,Model代表用户输入的类型,理论上可以用任意数据类型,主要的输入点在Glide.with().load(model)
Data理论上也可以是任意数据类型,但基于后续流程的支持,一般都是File,InputStreamByteBuffer;

ResourceDecoder

ResourceDecoder是一个解析Resource的接口,接受两个泛型<T,Z>,定义了两个方法handles()decode,参考一个简单的BitmapDecoder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {

private final Downsampler downsampler;
private final ArrayPool byteArrayPool;

public StreamBitmapDecoder(Downsampler downsampler, ArrayPool byteArrayPool) {
this.downsampler = downsampler;
this.byteArrayPool = byteArrayPool;
}

@Override
public boolean handles(@NonNull InputStream source, @NonNull Options options) {
return downsampler.handles(source);
}
@Override
public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
@NonNull Options options)
throws IOException {
...
try {
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
}

泛型解析:T表示输入类型一般为FileInputStream或者ByteBufferZ表示输出类型,一般为BitmapDrawable

Encoder和ResourceEncoder

Encoder表面意思为加密,本质上和加密没有关系,主要作用是将T持久化到本地cache;Encode接受泛型T,而这个T可以是InputStreamByteBufferResource<T>ResourceEncoder继承Encoder<Resource<T>>,接受泛型T,而Resource中的T一般取值范围为:BitmapBitmapDrawableGifDrawable;

我们分析一下把Bitmap持久化到本地cache的类:BitmapEncoder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class BitmapEncoder implements ResourceEncoder<Bitmap> {
@Override
public boolean encode(@NonNull Resource<Bitmap> resource, @NonNull File file,
@NonNull Options options) {
final Bitmap bitmap = resource.get();
Bitmap.CompressFormat format = getFormat(bitmap, options);
GlideTrace.
beginSectionFormat("encode: [%dx%d] %s", bitmap.getWidth(), bitmap.getHeight(), format);
try {
long start = LogTime.getLogTime();
int quality = options.get(COMPRESSION_QUALITY);

boolean success = false;
OutputStream os = null;
try {
os = new FileOutputStream(file);
if (arrayPool != null) {
os = new BufferedOutputStream(os, arrayPool);
}
bitmap.compress(format, quality, os);
os.close();
success = true;
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to encode Bitmap", e);
}
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// Do nothing.
}
}
}

return success;
} finally {
GlideTrace.endSection();
}
}
}

BitmapEncoder重写encode()方法,该方法中入参file代表将要保存的cache文件;encode基本流程是:根据File得到输出流OutPutStream,调用Bitmap.compress将bitmap写入输出流,然后关闭流等等处理;

DataRewinder

DataRewinder是一个接口,作用是将流进行rewinding,主要的方法是rewindAndGet();Glide中有两个有意义的实现类:InputStreamRewinderByteBufferRewinder,简单看一下这两个类是实现:

ByteBufferRewinder.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ByteBufferRewinder implements DataRewinder<ByteBuffer> {
private final ByteBuffer buffer;

@SuppressWarnings("WeakerAccess")
public ByteBufferRewinder(ByteBuffer buffer) {
this.buffer = buffer;
}

@NonNull
@Override
public ByteBuffer rewindAndGet() {
buffer.position(0);
return buffer;
}

@Override
public void cleanup() {
// Do nothing.
}

InputStreamRewinder.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class InputStreamRewinder implements DataRewinder<InputStream> {
// 5mb.
private static final int MARK_LIMIT = 5 * 1024 * 1024;

private final RecyclableBufferedInputStream bufferedStream;

@Synthetic
InputStreamRewinder(InputStream is, ArrayPool byteArrayPool) {
bufferedStream = new RecyclableBufferedInputStream(is, byteArrayPool);
bufferedStream.mark(MARK_LIMIT);
}

@NonNull
@Override
public InputStream rewindAndGet() throws IOException {
bufferedStream.reset();
return bufferedStream;
}

@Override
public void cleanup() {
bufferedStream.release();
}

ResourceTranscoder

ResourceTranscoder是一个接口,作用是对两个Resource<?>进行转换,接受泛型<Z,R>,主要方法是transcode(),一般泛型的接收范围是BitmapDrawablebyte[]等,看一下简单的BitmapBytesTranscoder源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class BitmapBytesTranscoder implements ResourceTranscoder<Bitmap, byte[]> {
private final Bitmap.CompressFormat compressFormat;
private final int quality;

public BitmapBytesTranscoder() {
this(Bitmap.CompressFormat.JPEG, 100);
}

@SuppressWarnings("WeakerAccess")
public BitmapBytesTranscoder(@NonNull Bitmap.CompressFormat compressFormat, int quality) {
this.compressFormat = compressFormat;
this.quality = quality;
}

@Nullable
@Override
public Resource<byte[]> transcode(@NonNull Resource<Bitmap> toTranscode,
@NonNull Options options) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
toTranscode.get().compress(compressFormat, quality, os);
toTranscode.recycle();
return new BytesResource(os.toByteArray());
}
}

加载/解析 数据转换流程

上面介绍一堆组件和一堆泛型,数据类型的转换到底是怎样?搞明白这一点还得从Rigistry提供的操作方法入手:

Registry提供getModelLoaders()getLoadPath(),我们先从定义方法的泛型来看:

1
2
3
4
5
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {...}

public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {...}

getModelLoader()分析

getModelLoaders()入参类型为<Model>,返回类型为<Model,?><Model>具体类型就是我们调用Glide.with().load(model)load()传入的类型,返回类型<?>是我们在Registry中注册的所有符合输入<Model>的类型,比如InputStream或者ByteBuffer

LoadPath()分析

LoadPath()入参类型为<Data, TResource, Transcode>,其中<Data>是在getModelLoaders()返回的类型,例如InputStream或者ByteBuffer<TResource>是待定类型,调用者一般传?,<Transcode>调用Glide.with().as(xxx)as()传入的类型,Glide提供有asBitmap(),asFile(),asGif(),默认是Drawable类型;在调用时<TResource>是待定类型,肯定有逻辑获取它的目标类型,下面分析getLoadPath()方法一看究竟;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
LoadPath<Data, TResource, Transcode> result =
loadPathCache.get(dataClass, resourceClass, transcodeClass);
if (loadPathCache.isEmptyLoadPath(result)) {
return null;
} else if (result == null) {
List<DecodePath<Data, TResource, Transcode>> decodePaths =
getDecodePaths(dataClass, resourceClass, transcodeClass);
if (decodePaths.isEmpty()) {
result = null;
} else {
result =
new LoadPath<>(
dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}

LoadPath()方法从loadPathCache获取缓存对象,如果不存在,调用getDecodePaths(),经过判断,创建LoadPath对象,将获取的结果放入LoadPath,最后放入loadPathCache并返回,LoadPath是对Data,TResource,TranscodeList<DecodePath<Data, TResource, Transcode>>的封装,最终的逻辑还是再DecodePath中;

看一下getDecodePaths()方法定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
@NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
//获取所有dataClass对应的ResourceClasses
List<Class<TResource>> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass);**代码1
//遍历registeredResourceClass
for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
//获取所有的registeredResourceClass对应的registeredTranscodeClasses
List<Class<Transcode>> registeredTranscodeClasses =
transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);**代码2
//遍历registeredTranscodeClasses
for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
//获取dataClass和registeredResourceClass对应的所有ResourceDecoder
List<ResourceDecoder<Data, TResource>> decoders =
decoderRegistry.getDecoders(dataClass, registeredResourceClass);**代码3
//获取registeredResourceClass和registeredTranscodeClasss对应的所有ResourceTranscoder
ResourceTranscoder<TResource, Transcode> transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);**代码4
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
//创建DecodePath,把相关信息封装
DecodePath<Data, TResource, Transcode> path =
new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass,
decoders, transcoder, throwableListPool);
//添加进集合
decodePaths.add(path);
}
}
//返回集合
return decodePaths;
}

为了更清楚的分析代码,我可以将假设泛型的类型为<InputStream,?,Drawable>

我在上面代码中做了标记:代码1通过调用transcoderRegistry.getTranscodeClasses(),返回的类型就是泛型?所未知的具体类型;

代码2通过调用transcoderRegistry.getTranscodeClasses(),返回所有符合条件的registeredTranscodeClasses;

代码3通过调用decoderRegistry.getDecoders()获取符合条件的List<ResourceDecoder>;

代码4通过调用transcoderRegistry.get()获取符合条件的ResourceTranscoder

DecodePath是对Data, TResource, Transcode,decoders,transcoder的封装;

第一层for循环理解
由于<TResource>是入参是未知类型,并不是用户定义的,是Registry模块支持的中间类型,它是靠入参类型<Data>进行筛选,所以就可能有可能有多个匹配;

第二层for循环理解
因为<Transcode>是用户传入,这个泛型是一个已确定类型,通常是Drawable,但是真正注册给transcoderRegistry可能是BitmapDrawable或则BitmapDrawable类型,这一刻还不确定是哪个Drawable,所以在这一步,registry返回给调用者多个;

总结:

加载过程是从getModelLoader()调用,数据从Model->Data;

解析过程是从getLoadPath()调用,中间经过decoder、transcoder,数据类型从Data->TResource->Transcode

写缓存数据转换流程

写缓存过程分为两类,一类是直接将原数据缓存,另一类是将变化后的数据写缓存,他们分别对应的是EncoderResourceEncoder;

Encoder流程

Encoder的使用场景在SourceGenerator.cacheData(dataToCache)方法中,最终通过调用Registry.getSourceEncoder()获取到Encode;

1
2
3
4
5
6
7
public <X> Encoder<X> getSourceEncoder(@NonNull X data) throws NoSourceEncoderAvailableException {
Encoder<X> encoder = encoderRegistry.getEncoder((Class<X>) data.getClass());
if (encoder != null) {
return encoder;
}
throw new NoSourceEncoderAvailableException(data.getClass());
}

上一节说过SourceGenerator是对原数据的获取,cacheData()中拿到的 dataToCache一般是加载过程返回的Data,确切的说是InputStream或者ByteBuffer类型,而Encoder最终保存到文件,类型为File,所以Encoder数据是从Data->File的;执行的时机在加载之后,和解析过程并列执行;

ResourceEncoder流程

ResourceEncoder的使用场景是在数据解析完毕后,将处理过的数据进行缓存,调用的地方在DecodeJob.onResourceDecoded()方法中,其最终通过调用Registry.getResultEncoder()获取;

1
2
3
4
5
6
7
8
public <X> ResourceEncoder<X> getResultEncoder(@NonNull Resource<X> resource)
throws NoResultEncoderAvailableException {
ResourceEncoder<X> resourceEncoder = resourceEncoderRegistry.get(resource.getResourceClass());
if (resourceEncoder != null) {
return resourceEncoder;
}
throw new NoResultEncoderAvailableException(resource.getResourceClass());
}

ResourceEncoder数据类型是从Resource<X>->File;执行的时机在解析流程之后;

总结

最后画一张数据简单的数据装换流程图

CATALOG
  1. 1. 前言
  2. 2. 从Registry开始
  3. 3. 模块简要分析
    1. 3.1. ModelLoader
    2. 3.2. ResourceDecoder
    3. 3.3. Encoder和ResourceEncoder
    4. 3.4. DataRewinder
    5. 3.5. ResourceTranscoder
  4. 4. 加载/解析 数据转换流程
    1. 4.1. getModelLoader()分析
    2. 4.2. LoadPath()分析
    3. 4.3. 总结:
  5. 5. 写缓存数据转换流程
    1. 5.1. 总结