• Glide源码解析四(解码和转码)


    本文基于Glide 4.11.0

    Glide加载过程有一个解码过程,比如将url加载为inputStream后,要将inputStream解码为Bitmap。

     

    Glide源码解析一我们大致知道了Glide加载的过程,所以我们可以直接从这里看起,在这个过程中我们以从文件中加载bitmap为例:

    DecodeJob的一个方法:

    复制代码
    private void decodeFromRetrievedData() {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Retrieved data", startFetchTime,
            "data: " + currentData
                + ", cache key: " + currentSourceKey
                + ", fetcher: " + currentFetcher);
      }
      Resource resource = null;
      try {
        resource = decodeFromData(currentFetcher, currentData, currentDataSource);
      } catch (GlideException e) {
        e.setLoggingDetails(currentAttemptingKey, currentDataSource);
        throwables.add(e);
      }
      if (resource != null) {
        notifyEncodeAndRelease(resource, currentDataSource);
      } else {
        runGenerators();
      }
    }
    复制代码

    主要是这个方法:resource = decodeFromData(currentFetcher, currentData, currentDataSource);

    这时候currentData为FileInputStream,因为我们加载的是本地文件。 

    currentDateSource为LOCAL,即为本地的资源

     

     

     我们继续找下去

    resource = decodeFromData(currentFetcher, currentData, currentDataSource);

    ----------------->

    Resource result = decodeFromFetcher(data, dataSource);

    ------------------>

    private  Resource decodeFromFetcher(Data data, DataSource dataSource)
        throws GlideException {
      LoadPath path = decodeHelper.getLoadPath((Class) data.getClass());
      return runLoadPath(data, dataSource, path);

    这里获取到LoadPath的对象,我么先看看LoadPath有什么?

    我们可以看到一个DecodePaths:

     

     DecodePath里面又保存着decoders

     

    decoders便是我们需要的解码器,拿到解码器后就可以进行解码了。

    那怎么拿到?

    Glide源码解析三中我们知道这些解码器都注册在Register中,所以我们也是要通过它来拿:

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

    ---------------->

     

    复制代码
    @Nullable
    public  LoadPath getLoadPath(
        @NonNull Class dataClass, @NonNull Class resourceClass,
        @NonNull Class transcodeClass) {
      LoadPath result =
          loadPathCache.get(dataClass, resourceClass, transcodeClass);
      if (loadPathCache.isEmptyLoadPath(result)) {
        return null;
      } else if (result == null) {
        List> decodePaths =
            getDecodePaths(dataClass, resourceClass, transcodeClass);
        // It's possible there is no way to decode or transcode to the desired types from a given
        // data class.
        if (decodePaths.isEmpty()) {
          result = null;
        } else {
          result =
              new LoadPath<>(
                  dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
        }
        loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
      }
      return result;
    }
    复制代码

    首先会先从缓存中拿,缓存中拿不到再通过下面的方法去拿:

    List> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass);

     

    复制代码
    private  List> getDecodePaths(
        @NonNull Class dataClass, @NonNull Class resourceClass,
        @NonNull Class transcodeClass) {
      List> decodePaths = new ArrayList<>();
      List> registeredResourceClasses =
          decoderRegistry.getResourceClasses(dataClass, resourceClass);
      for (Class registeredResourceClass : registeredResourceClasses) {
        List> registeredTranscodeClasses =
            transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
        for (Class registeredTranscodeClass : registeredTranscodeClasses) {
          List> decoders =
              decoderRegistry.getDecoders(dataClass, registeredResourceClass);
          ResourceTranscoder transcoder =
              transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
          @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
          DecodePath path =
              new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass,
                  decoders, transcoder, throwableListPool);
          decodePaths.add(path);
        }
      }
      return decodePaths;
    }
    复制代码

     该方法各个参数如下:

    dataClass为InputStream,这是被解码的对象

     

    resourceClass为Object,要解码成为Object

     

     

    transcodeClass为Drawable,要转码为Drawable

    我们看这个方法:

    decoderRegistry.getResourceClasses:

     

    复制代码
    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;
    }
    复制代码

    该方法是为了获取解码器中的resourceClass,即解码后的资源类型。

    我们可以看到decoder这个map里面的内容:

     

    各种类型对应的解码器。

     

    只有满足entry.handles(dataClass, resourceClass),才能被添加返回:

    public boolean handles(@NonNull Class dataClass, @NonNull Class resourceClass) {
      return this.dataClass.isAssignableFrom(dataClass) && resourceClass
          .isAssignableFrom(this.resourceClass);
    }

    由于我们的resourceClass是Object,因此resourceClass .isAssignableFrom(this.resourceClass)总是成立的,所以就看:this.dataClass.isAssignableFrom(dataClass)

    而我们的dataClass是InputStream,打开各种类型,可以看到哪些的dataClass是InputStream:

     

    上面框错了,应该框resourceClass,另外FrameSequenceDrawable是我自定义后注册进去的,所以Glide原生的是没有的。

    所以最终返回的resource为:

     

     

    接下来是针对每一种resourceClass获取对应的转码类(要转成的对象):

    复制代码
    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)) {
        transcodeClasses.add(transcodeClass);
        return transcodeClasses;
      }
      for (Entry entry : transcoders) {
        if (entry.handles(resourceClass, transcodeClass)) {
          transcodeClasses.add(transcodeClass);
        }
      }
      return transcodeClasses;
    }
    复制代码

    如果transcodeClass是resourceClass的父类那就直接返回。

    第一个GifDrawable,返回的registeredTranscodeClasses为:

     

     

    然后根据dataClass, registeredResourceClass获取decoders:

     

     

    然后根据registeredResourceClass和registeredTranscodeClass获取transcoder

     

    上面具体的获取过程是类似的,就不过多分析了。

     

    然后构造DecodePath,放进下面的集合里面:

    List> decodePaths = new ArrayList<>();

     

    循环获取之后,最终得到的decodePaths如下:

     

     

    大致流程:

    1、先根据传进来的resourceClass获取注册表中所有注册的resourceClass得到List> registeredResourceClasses

    2、两层for循环:

       (1)外层:根据registeredResourceClasses获取转码的class :List> registeredTranscodeClasses

       (2)内层:

                a、根据资源resourceClass获取所有的解码器。

                b、根据资源resourceClass和转码transcodeClass获取所有的转码器。

                c、构造DecodePath,放进集合里面。

     

    最后得到的List> decodePaths被放到LoadPath对象里面(上一层方法可看到)

     

     我们又回到DecodeJob中的方法:

    复制代码
    private  Resource decodeFromFetcher(Data data, DataSource dataSource)
        throws GlideException {
      LoadPath path = decodeHelper.getLoadPath((Class) data.getClass());
      return runLoadPath(data, dataSource, path);
    }
    复制代码

    获取到LoadPath后接下来就是要开始执行了runLoadPath了。

     

    找下去可以看到该方法:

    return path.load(
              rewinder, options, width, height, new DecodeCallback(dataSource));

    该方法属于LoadPath对象。

     

    层层追溯后,最终来到下面的方法:

    复制代码
    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);
        } catch (GlideException e) {
          exceptions.add(e);
        }
        if (result != null) {
          break;
        }
      }
      if (result == null) {
        throw new GlideException(failureMessage, new ArrayList<>(exceptions));
      }
      return result;
    }
    复制代码

    该方法在LoadPath里面,遍历decodePaths(这是我们之前获取后放在LoadPath中的)进行解码:

    result = path.decode(rewinder, width, height, options, decodeCallback);

    然后来到:

    复制代码
    public Resource decode(DataRewinder rewinder, int width, int height,
        @NonNull Options options, DecodeCallback callback) throws GlideException {
      Resource decoded = decodeResource(rewinder, width, height, options);
      Resource transformed = callback.onResourceDecoded(decoded);
      return transcoder.transcode(transformed, options);
    }
    复制代码

    我们这里需要看的就是:decodeResource:

    最终来到DecodePath里面的方法:

    复制代码
    @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);
          }
          // Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but
          // instead log and continue. See #2406 for an example.
        } catch (IOException | RuntimeException | OutOfMemoryError e) {
          if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "Failed to decode data for " + decoder, e);
          }
          exceptions.add(e);
        }
        if (result != null) {
          break;
        }
      }
      if (result == null) {
        throw new GlideException(failureMessage, new ArrayList<>(exceptions));
      }
      return result;
    }
    复制代码

    这个方法:decoder.handles(data, options)是判断该解码器是否可以对该资源进行解码,这个方法写在每个解码器里面。

    DataRewinder里面放着需要进行解码的数据。

    解码后将资源返回。

     

    又回到这个方法:

    复制代码
    public Resource decode(DataRewinder rewinder, int width, int height,
        @NonNull Options options, DecodeCallback callback) throws GlideException {
      Resource decoded = decodeResource(rewinder, width, height, options);
      Resource transformed = callback.onResourceDecoded(decoded);
      return transcoder.transcode(transformed, options);
    }
    复制代码

    这一句Resource transformed = callback.onResourceDecoded(decoded);

    是对资源进行变换处理,比如图片的缩放,剪裁等等,这个功能单独拎出来讲。

    接下来便是运用转码器进行资源的转码:

    transcoder.transcode(transformed, options)

     

    到此就结束了。

     

    转载请标明:https://www.cnblogs.com/tangZH/p/12912698.html

  • 相关阅读:
    微服务--数据一致性
    docker网络模式、资源控制
    (3)(3.5) 遥测无线电区域条例
    1024共码未来(一览中华风华,API First)
    巧用CSS3之雨伞
    在Java的JSP中返回JSON格式数据
    LuatOS-SOC接口文档(air780E)--nimble - 蓝牙BLE库(nimble版)
    CentOS 7文件系统中的软链接和硬链接
    微服务框架 SpringCloud微服务架构 10 使用Docker 10.6 容器命令练习
    c#常用的数据结构
  • 原文地址:https://www.cnblogs.com/tangZH/p/12912698.html