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


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

查看所有标签

猜你喜欢:

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

Automate This

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

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具