首页 > 编程语言 >Glide源码解析四(解码和转码)

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

时间:2024-03-03 14:01:25浏览次数:32  
标签:Glide 转码 List transcodeClass 源码 result dataClass resourceClass Class

本文基于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<R> 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<R> result = decodeFromFetcher(data, dataSource);

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

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

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

我们可以看到一个DecodePaths:

 

 DecodePath里面又保存着decoders

 

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

那怎么拿到?

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

<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
  return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}

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

 

@Nullable
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);
    // 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<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass);

 

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<>();
  List<Class<TResource>> registeredResourceClasses =
      decoderRegistry.getResourceClasses(dataClass, resourceClass);
  for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
    List<Class<Transcode>> registeredTranscodeClasses =
        transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
    for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
      List<ResourceDecoder<Data, TResource>> decoders =
          decoderRegistry.getDecoders(dataClass, registeredResourceClass);
      ResourceTranscoder<TResource, Transcode> transcoder =
          transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
      @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
      DecodePath<Data, TResource, Transcode> 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 <T, R> List<Class<R>> getResourceClasses(@NonNull Class<T> dataClass,
    @NonNull Class<R> resourceClass) {
  List<Class<R>> result = new ArrayList<>();
  for (String bucket : bucketPriorityList) {
    List<Entry<?, ?>> entries = decoders.get(bucket);
    if (entries == null) {
      continue;
    }
    for (Entry<?, ?> entry : entries) {
      if (entry.handles(dataClass, resourceClass)
          && !result.contains((Class<R>) entry.resourceClass)) {
        result.add((Class<R>) 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 <Z, R> List<Class<R>> getTranscodeClasses(
    @NonNull Class<Z> resourceClass, @NonNull Class<R> transcodeClass) {
  List<Class<R>> 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<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();

 

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

 

 

大致流程:

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

2、两层for循环:

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

   (2)内层:

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

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

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

 

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

 

 我们又回到DecodeJob中的方法:

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

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

 

找下去可以看到该方法:

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

该方法属于LoadPath对象。

 

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

private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
    @NonNull Options options,
    int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
    List<Throwable> exceptions) throws GlideException {
  Resource<Transcode> result = null;
  //noinspection ForLoopReplaceableByForEach to improve perf
  for (int i = 0, size = decodePaths.size(); i < size; i++) {
    DecodePath<Data, ResourceType, Transcode> 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<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
    @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
  Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
  Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
  return transcoder.transcode(transformed, options);
}

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

最终来到DecodePath里面的方法:

@NonNull
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
    int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
  Resource<ResourceType> result = null;
  //noinspection ForLoopReplaceableByForEach to improve perf
  for (int i = 0, size = decoders.size(); i < size; i++) {
    ResourceDecoder<DataType, ResourceType> 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<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
    @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
  Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
  Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
  return transcoder.transcode(transformed, options);
}

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

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

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

transcoder.transcode(transformed, options)

 

到此就结束了。

 

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

标签:Glide,转码,List,transcodeClass,源码,result,dataClass,resourceClass,Class
From: https://www.cnblogs.com/tangZH/p/12912698.html

相关文章

  • Qt/C++音视频开发67-保存裸流加入sps/pps信息/支持264/265裸流/转码保存/拉流推流
    一、前言音视频组件除了支持保存MP4文件外,同时还支持保存裸流即264/265文件,以及解码后最原始的yuv文件。在实际使用过程中,会发现部分视频文件保存的裸流文件,并不能直接用播放器播放,查阅资料得知原来是缺少sps/pps信息,监控行业的rtsp/rtmp/录像mp4文件都是会带的,所以很少遇到这个......
  • 【Mybatis】【三】源码分析- MapperFactoryBean 的创建过程以及 Mapper 接口代理的生
    1 前言本节我们续前两节(调试查看Mapper接口生成过程、源码分析Mapper生成注入入口分析)的内容,看下MapperFactoryBean是如何代理掉我们的@Mapper接口的。上节我们看到我们的Mapper接口的BeanDefinition,已经放进spring的上下文中了,也就是在BeanFactory的BeanDefin......
  • 网狐核心源码阅读分析
    框架结构网狐服务器整体上分为4个部分:中转服务器,房间服务器,大厅服务器,sqlserver数据库。其中大厅服务器主要负责帐号管理器:管理用户选择服务器登录地址,校验用户数据等。必需与中转服务器保持长连接,用于更新获取最新数据。房间服务器:用于加载处理每款子游戏逻辑与公共游戏逻辑(例......
  • 直播app系统源码,Android端如何实现禁止截屏或录屏
    直播app系统源码,Android端如何实现禁止截屏或录屏引言相信大家在使用某些平台应用的时候,都会有限制的规定。通常情况下,录屏、截图软件都可以在手机的运行过程中进行录屏、截图,普通的平台也不会阻止录屏、截图软件运行。但是在直播app系统源码的某些比较敏感的业务上镜上面......
  • 直播系统app源码,Android端与屏幕相关的几个注意事项
    直播系统app源码,Android端与屏幕相关的几个注意事项Android端的宽屏适配、禁止截屏和保持屏幕常亮,是直播系统app源码开发时需要注意的三个重要事项。宽屏适配越来越多的手机厂商趋向于全面屏设计,屏幕比例均超过过去常见的16:9比例。超大屏幕比例的设计对于AndroidAp......
  • Vue源码解读:响应式原理
    Vue一大特点就是数据响应式,数据的变化会作用于视图而不用进行DOM操作。原理上来讲,是利用了Object.defifineProperty(),通过定义对象属性setter方法拦截对象属性的变更,从而将属性值的变化转换为视图的变化。在Vue初始化时,会调用initState,它会初始化props,methods,data,......
  • Vue源码解读:手写一个简易版Vue
    Vue源码解读:手写一个简易版Vue</h1><divclass="clear"></div><divclass="postBody"><divid="cnblogs_post_body"class="blogpost-bodycnblogs-markdown">MVVM......
  • ConcurrentHashMap 核心源码解析
    废话不多说,直接看代码类名与HashMap很相似,数组、链表结构几乎相同,都实现了Map接口,继承了AbstractMap抽象类,大多数的方法也都是相同的publicclassConcurrentHashMap<K,V>extendsAbstractMap<K,V>implementsConcurrentMap<K,V>,Serializable核心方法Node方法......
  • Vue源码解读(预):手写一个简易版Vue
    Vue源码解读(预):手写一个简易版Vue MVVM设计模式,是由MVC、MVP等设计模式进化而来,M-数据模型(Model),VM-视图模型(ViewModel),V-视图层(View)。MVVM的核心是ViewModel层,它就像是一个中转站(valueconverter),负责转换Model中的数据对象来让数据变得更容易管理和使用,该层向上与视......
  • Eureka源码分析
     注册中心1、Register:服务注册  当Eureka客户端向EurekaServer注册时,它提供自身的元数据,比如IP地址、端口,运行状况指示符URL,主页等    1.1、服务端注册    会拉取配置的注册中心地址,向附近注册服务注册    1.2、客户端注册客户端第一次续约会失败,因......