Glide最新源码解析(四)- 解码和转码流程

介绍

从上一篇文章我们知道了如何从Model得到Data。拿到Data数据后还要经过解码和转码才能得到我们app上需要的资源,这篇文章分析下解码,转换 ,转码的工作流程。
先说明一下这几个过程的作用:

  • 解码过程就是通过Data(InputStream等)得到Resource(bitmap等)。
  • 转换过程就是将Bitmap经过ImageView 的ScaleTyp转为特定的样式。
  • 转码过程就是把BitmapResource转换为高层模块需要的DrawableResource。

准备工作

首先需要根据传入的Data(InputStream) Resource(Object) 和transcodeResource(Drawable)找到后边解码和转码需要的资源和解码器和转码器
DecodeJob.java

 @Override
  public void onDataFetcherReady(
      Key sourceKey, Object data, DataFetcher fetcher, DataSource dataSource, Key attemptedKey) {
    this.currentSourceKey = sourceKey;
    this.currentData = data;
    this.currentFetcher = fetcher;
    this.currentDataSource = dataSource;
    this.currentAttemptingKey = attemptedKey;
    if (Thread.currentThread() != currentThread) {
      runReason = RunReason.DECODE_DATA;
      callback.reschedule(this);
    } else {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        decodeFromRetrievedData();
      } finally {
        GlideTrace.endSection();
      }
    }
  }
 /**从接收到的数据解码*/
  private void decodeFromRetrievedData() {
    Resource resource = null;
     ...
    resource = decodeFromData(currentFetcher, currentData, currentDataSource);//1解码
     ...
    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);//1 通知编码和释放资源
    } else {
      runGenerators();
    }
  }
  /**使用DataFetcher 根据Data解码资源*/
  private  Resource decodeFromData(
      DataFetcher fetcher, Data data, DataSource dataSource) throws GlideException {
    try {
      ...
      Resource result = decodeFromFetcher(data, dataSource);
      ...
      return result;
    } finally {
      fetcher.cleanup();
    }
  }

  private  Resource decodeFromFetcher(Data data, DataSource dataSource)
      throws GlideException {
    LoadPath path = decodeHelper.getLoadPath((Class) data.getClass());//3查找LoadPath
    return runLoadPath(data, dataSource, path);
  }
  
  private  Resource runLoadPath(
      Data data, DataSource dataSource, LoadPath path)
      throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    DataRewinder rewinder = glideContext.getRegistry().getRewinder(data);//1
   ....
    return path.load(
         rewinder, options, width, height, new DecodeCallback(dataSource));//2
  ....
  }

我们看一下注释3处的查找LoadPath,这个在前边的SourceGenerator中startNext()方法内helper.hasLoadPath(loadData.fetcher.getDataClass()))的一样的,所以放到这里分析。DecodeHelper.java

  LoadPath getLoadPath(Class dataClass) {//Inputstream.class
    return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
  }

根据dataClass, resourceClass, transcodeClass从注册表中查找所有需要的资源

Registry#getLoadPath

  @Nullable
  public  LoadPath getLoadPath(
      @NonNull Class dataClass,//InputStream.class
      @NonNull Class resourceClass,//Object.class
      @NonNull Class transcodeClass) {//Drawable.class
    LoadPath result =
        loadPathCache.get(dataClass, resourceClass, transcodeClass);
    ...
      List> decodePaths =
          getDecodePaths(dataClass, resourceClass, transcodeClass);
        ...
        result =
            new LoadPath<>(
                dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
      loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
    }
    return result;
  }
@NonNull
  private  List> getDecodePaths(
      @NonNull Class dataClass,//inputstream
      @NonNull Class resourceClass,//object
      @NonNull Class transcodeClass) {//Darwable
    List> decodePaths = new ArrayList<>();
    List> registeredResourceClasses =
        decoderRegistry.getResourceClasses(dataClass, resourceClass);//1

    for (Class registeredResourceClass : registeredResourceClasses) {
      List> registeredTranscodeClasses =
          transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);//2

      for (Class registeredTranscodeClass : registeredTranscodeClasses) {

        List> decoders =
            decoderRegistry.getDecoders(dataClass, registeredResourceClass);//3

        ResourceTranscoder transcoder =
            transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);//4
        @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
        DecodePath path =
            new DecodePath<>(
                dataClass,
                registeredResourceClass,
                registeredTranscodeClass,
                decoders,
                transcoder,
                throwableListPool);//5
        decodePaths.add(path);
      }
    }
    return decodePaths;
  }

getDecodePaths方法根据参数从注册表中查询到所有符合条件的DecodePath集合。这里的参数分别是dataClass=InputStream.class resourceClass = Object.class transcodeClass = Drawable.class
注释1处decoderRegistry根据dataClass 和resourceClass 找到所有的资源类.如下是相关代码ResourceDecoderRegistry.java

...
  @NonNull
  @SuppressWarnings("unchecked")
  public synchronized  List> getResourceClasses(
      @NonNull Class dataClass, @NonNull Class resourceClass) {
    List> result = new ArrayList<>();
    for (String bucket : bucketPriorityList) {
      List> entries = decoders.get(bucket);
      if (entries == null) {
        continue;
      }
      for (Entry entry : entries) {
        if (entry.handles(dataClass, resourceClass)
            && !result.contains((Class) entry.resourceClass)) {//去重
          result.add((Class) entry.resourceClass);
        }
      }
    }
    return result;
  }
...
private static class Entry {
  public boolean handles(@NonNull Class dataClass, @NonNull Class resourceClass) {
        return this.dataClass.isAssignableFrom(dataClass)
            && resourceClass.isAssignableFrom(this.resourceClass);//resourceClass是this.resourceClass基类或  者相同
    }  
}

这里的resourceClass 是Object.class,是所有类的基类,所以注册表中的所有类型都符合条件,由于前边去重的作用,所以registeredResourceClasses 集合中是GifDrawable.class Bitmap.class BitmapDrawable.class.
Registry#getDecodePaths方法的注释2处根据注册的资源类和要求的转码类(Drawable.class)
TranscoderRegistry#getTranscodeClasses方法

  @NonNull
  public synchronized  List> getTranscodeClasses(
      @NonNull Class resourceClass, @NonNull Class transcodeClass) {
    List> transcodeClasses = new ArrayList<>();
    // GifDrawable -> Drawable is just the UnitTranscoder, as is GifDrawable -> GifDrawable.
    if (transcodeClass.isAssignableFrom(resourceClass)) {//1
      transcodeClasses.add(transcodeClass);
      return transcodeClasses;
    }

    for (Entry entry : transcoders) {
      if (entry.handles(resourceClass, transcodeClass)) {//2
        transcodeClasses.add(transcodeClass);//3
      }
    }

    return transcodeClasses;
  }

第一次GifDrawable.class 是 Drawable.class的子类,所以执行注释1,直接返回transcodeClasses 集合只有Drawable.class
第二次执行注释2,根据resourceClass, transcodeClass从注册表中查找transcodeClass,Glide中的注册表中条件满足的是

.register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))

注意注释3处返回的是传进来的Drawable.class,所以第二次的集合只有一个是entry.resourceClass 即 Drawable.class

第三次BitmapDrawable.class.是Drawable.class的子类所以执行注释1,直接返回transcodeClasses 集合只有Drawable.class
接着是Registry#getDecodePaths方法的注释3的地方根据dataClass和注册的资源类从解码表中找到解码器集合。ResourceDecoderRegistry的代码如下:

  /**
   * 得到资源解码器
   * @param dataClass 数据类(byteBuffer,stream等)
   * @param resourceClass 资源类(bitmap,drawable等)
   * @param  T
   * @param  R
   * @return 解码器集合
   */
  @NonNull
  @SuppressWarnings("unchecked")
  public synchronized  List> getDecoders(
      @NonNull Class dataClass, @NonNull Class resourceClass) {
    List> result = new ArrayList<>();
    for (String bucket : bucketPriorityList) {
       ...
      for (Entry entry : entries) {
        if (entry.handles(dataClass, resourceClass)) {
          result.add((ResourceDecoder) entry.decoder);
        }
      }
    }

    return result;
  }

这里从Glide注册的资源解码器中查找符合条件的,结果如下:
第一次:Inputstream.class->GifDrawable.class 解码器是StreamGifDecoder对象
第二次:Inputstream.class->Bitmap.class 解码器是streamBitmapDecoder对象
第三次:Inputstream.class->BitmapDrawable.class 解码器是BitmapDrawableDecoder对象
Registry#getDecodePaths方法的注释4处根据注册的资源类和注册的解码类通过注册表查找资源转码器
ResourceTranscoder。TranscoderRegistry#get

  @NonNull
  @SuppressWarnings("unchecked")
  public synchronized  ResourceTranscoder get(
      @NonNull Class resourceClass, @NonNull Class transcodedClass) {
    if (transcodedClass.isAssignableFrom(resourceClass)) {//1 gifDrawable-Drawable
      return (ResourceTranscoder) UnitTranscoder.get();
    }
    for (Entry entry : transcoders) {
      if (entry.handles(resourceClass, transcodedClass)) {//2
        return (ResourceTranscoder) entry.transcoder;
      }
    }

    throw new IllegalArgumentException(
        "No transcoder registered to transcode from " + resourceClass + " to " + transcodedClass);
  }

for循环结果是:
第一次:GifDrawable->Drawable 满足注释1 得到是UnitTranscoder
第二次:Bitmap->Drawable 满足注释2 从注册表中找到的是BitmapDrawableTranscoder
第三次:BitmapDrawable->Drawable 满足注释1 得到是UnitTranscoder

getDecodePaths方法的注释5根据前边查询的参数组装DecodePath对象并且放入集合decodePaths中
所以总结一下最后的到的结果,伪代码是:

List> decodePaths = new ArrayList<>();
decodePaths .add(new DecodePath<>(
InputStream.class,
GifDrawable.class,
Drawable.class,
StreamGifDecoder.class,
UnitTranscoder.class,
throwableListPool);

decodePaths .add(new DecodePath<>(
InputStream.class,
Bitmap.class,
Drawable.class,
StreamBitmapDecoder.class,
BitmapDrawableTranscoder.class,
throwableListPool);

decodePaths .add(new DecodePath<>(
InputStream.class,
BitmapDrawable.class,
Drawable.class,
BitmapDrawableDecoder.class,
UnitTranscoder.class,
throwableListPool);

执行解码和编码

回到DecodeJob的runLoadPath注释1处查找DataRewinder 得到是InputStreamRewinder对象。然后是主要的load工作,即注释2处会执行LoadPath#loadWithExceptionList

 private Resource loadWithExceptionList(
      DataRewinder rewinder,
      @NonNull Options options,
      int width,
      int height,
      DecodePath.DecodeCallback decodeCallback,
      List exceptions)
      throws GlideException {
    Resource result = null;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath path = decodePaths.get(i);
      try {
        result = path.decode(rewinder, width, height, options, decodeCallback);//1
      } catch (GlideException e) {
        exceptions.add(e);
      }
      if (result != null) {//2
        break;
      }
    }

    if (result == null) {
      throw new GlideException(failureMessage, new ArrayList<>(exceptions));
    }

    return result;
  }

DecodePath#decode

  public Resource decode(
      DataRewinder rewinder,
      int width,
      int height,
      @NonNull Options options,
      DecodeCallback callback)
      throws GlideException {
    //解码资源
    Resource decoded = decodeResource(rewinder, width, height, options);
    //2 转换资源(CENTER_CROP等)
    Resource transformed = callback.onResourceDecoded(decoded);
    //3.转码(Bitmap=BitmapDrawable)
    return transcoder.transcode(transformed, options);
  }

解码资源的代码是:

 @NonNull
  private Resource decodeResource(
      DataRewinder rewinder, int width, int height, @NonNull Options options)
      throws GlideException {
    List exceptions = Preconditions.checkNotNull(listPool.acquire());
    try {
      return decodeResourceWithList(rewinder, width, height, options, exceptions);
    } finally {
      listPool.release(exceptions);
    }
  }

  @NonNull
  private Resource decodeResourceWithList(
      DataRewinder rewinder,
      int width,
      int height,
      @NonNull Options options,
      List exceptions)
      throws GlideException {
    Resource result = null;
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = decoders.size(); i < size; i++) {
      ResourceDecoder decoder = decoders.get(i);
      try {
        DataType data = rewinder.rewindAndGet();
        if (decoder.handles(data, options)) {
          data = rewinder.rewindAndGet();
          result = decoder.decode(data, width, height, options);
        }
        ...
      }

      if (result != null) {//3
        break;
      }
    }

    if (result == null) {
      throw new GlideException(failureMessage, new ArrayList<>(exceptions));
    }
    return result;
  }

根据前边的分析decoders第一个是StreamGifDecoder,它的decode得到的是null。
decoders第二个是StreamBitmapDecoder,它的decode的到result 是Bimap,即result!=null,所以注释3处结束循环,返回result。

转换 裁剪缩放成需要的样式的资源

DecodePath#decode的注释2处会回调decodeJob中的onResourceDecoded,方法内会对资源进行转换,并且把转换后的资源写入磁盘缓存中。

解码

DecodePath#decode的注释2处会将bitmap转为BitmapDrawable 资源类型
转码器BitmapDrawableTranscoder#transcode方法

  @Nullable
  @Override
  public Resource transcode(
      @NonNull Resource toTranscode, @NonNull Options options) {
    return LazyBitmapDrawableResource.obtain(resources, toTranscode);
  }

LazyBitmapDrawableResource.java

  @Nullable
  public static Resource obtain(
      @NonNull Resources resources, @Nullable Resource bitmapResource) {
    if (bitmapResource == null) {
      return null;
    }
    return new LazyBitmapDrawableResource(resources, bitmapResource);
  }

  private LazyBitmapDrawableResource(
      @NonNull Resources resources, @NonNull Resource bitmapResource) {
    this.resources = Preconditions.checkNotNull(resources);
    this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
  }

最后通过转码得到的是LazyBitmapDrawableResource 资源类。加载的结果怎么处理,上一篇已经讲过了。

你可能感兴趣的:(Glide最新源码解析(四)- 解码和转码流程)