内容简介: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 虚拟机
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP 5完全攻略
杜江 / 2010-5 / 79.00元
《PHP 5完全攻略(畅销书升级版)》是目前第一本真正介绍PHP 5及MySQL 5新增语法与功能的中文版本权威宝典!《PHP 5完全攻略(畅销书升级版)》本着精、全、要三宗旨,从理论中延伸,从实践中深入,翔实并完善地描述了PHP 5的开发特性与MySQL 5数据库。《PHP 5完全攻略(畅销书升级版)》分为两大部分,第1部分主要阐述PHP开发的基础知识,如PHP数组与表单处理、PHP 5面向对象......一起来看看 《PHP 5完全攻略》 这本书的介绍吧!