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

栏目: IOS · Android · 发布时间: 5年前

内容简介: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 , InputStreamByteBuffer ;

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 表示输入类型一般为 FileInputStream 或者 ByteBufferZ 表示输出类型,一般为 BitmapDrawable

Encoder和ResourceEncoder

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

我们分析一下把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中有两个有意义的实现类: InputStreamRewinderByteBufferRewinder ,简单看一下这两个类是实现:

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() ,一般泛型的接收范围是 BitmapDrawablebyte[] 等,看一下简单的 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 , TranscodeList<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

写缓存数据转换流程

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

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 ;执行的时机在解析流程之后;


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

PHP 5完全攻略

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完全攻略》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

SHA 加密
SHA 加密

SHA 加密工具