内容简介:Registry是Glide中非常重要的知识,可以把它理解成连结各个核心功能模块的集中营或者挂载中心,这一章节就来分解它是如何建立和运作的:本章要讨论的内容:Registry是一个组件管理类,它的主要用途是扩展和替换Glide组件,这些组件包括加载,编码,解码等逻辑;Registry内部支持的模块类型如下:
Registry是Glide中非常重要的知识,可以把它理解成连结各个核心功能模块的集中营或者挂载中心,这一章节就来分解它是如何建立和运作的:
本章要讨论的内容:
- Registry的基本构成;
- 各个模块的功能和介绍;
- 数据的转换流程;
从Registry开始
Registry是一个组件管理类,它的主要用途是扩展和替换Glide组件,这些组件包括加载,编码,解码等逻辑;Registry内部支持的模块类型如下: Registry.java
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()方法;
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()方法;
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对象;
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的注册相关方法;
@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
, InputStream
和 ByteBuffer
;
ResourceDecoder
ResourceDecoder
是一个解析Resource的接口,接受两个泛型 <T,Z>
,定义了两个方法 handles()
和 decode
,参考一个简单的 BitmapDecoder
:
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
表示输入类型一般为 File
, InputStream
或者 ByteBuffer
, Z
表示输出类型,一般为 Bitmap
和 Drawable
Encoder和ResourceEncoder
Encoder
表面意思为加密,本质上和加密没有关系,主要作用是将T持久化到本地cache; Encode
接受泛型 T
,而这个 T
可以是 InputStream
、 ByteBuffer
、 Resource<T>
, ResourceEncoder
继承 Encoder<Resource<T>>
,接受泛型 T
,而 Resource
中的 T
一般取值范围为: Bitmap
、 BitmapDrawable
、 GifDrawable
;
我们分析一下把Bitmap持久化到本地cache的类: BitmapEncoder
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中有两个有意义的实现类: InputStreamRewinder
和 ByteBufferRewinder
,简单看一下这两个类是实现:
ByteBufferRewinder.java
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
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()
,一般泛型的接收范围是 Bitmap
、 Drawable
、 byte[]
等,看一下简单的 BitmapBytesTranscoder
源码:
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()
,我们先从定义方法的泛型来看:
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()
方法一看究竟;
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
, Transcode
和 List<DecodePath<Data, TResource, Transcode>>
的封装,最终的逻辑还是再 DecodePath
中;
看一下 getDecodePaths()
方法定义:
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
写缓存数据转换流程
写缓存过程分为两类,一类是直接将原数据缓存,另一类是将变化后的数据写缓存,他们分别对应的是 Encoder
和 ResourceEncoder
;
Encoder流程
Encoder
的使用场景在 SourceGenerator.cacheData(dataToCache)
方法中,最终通过调用 Registry.getSourceEncoder()
获取到 Encode
;
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()
获取;
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
;执行的时机在解析流程之后;
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Glide4.8源码拆解(二)核心加载流程
- 一套完整的 Tomcat 和 Jetty 的源码拆解
- Glide4.8源码拆解(五)BitmapPool从入门到放弃
- Glide4.8源码拆解(四)Bitmap解析之"下采样"浅析
- Dubbo 源码解析之 SPI(一):拆解扩展类的加载过程
- 深入拆解 Java 虚拟机
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Automate This
Christopher Steiner / Portfolio / 2013-8-9 / USD 25.95
"The rousing story of the last gasp of human agency and how today's best and brightest minds are endeavoring to put an end to it." It used to be that to diagnose an illness, interpret legal docume......一起来看看 《Automate This》 这本书的介绍吧!