注册

Glide数据输入输出

基础概念


在正式开始之前先明确一些概念


Glide输入: 我们日常在使用Glide的时候,通过load可以加载不同的资源类型例如文件,字符串等待。


requestManager多个load重载.png


我们把load的不同类型称为不同的输入。


Glide输出: Glide RequestManager提供了许多的as重载方法,


GlideAs方法.png


通过不同的as我们可以指定不同的输出类型。


ModelLoader: 是一个泛型接口,最直观的翻译是模型加载器。ModelLoader标记了它能够加载什么类型的数据,以及加载后返回什么样的数据类型。注意这里说说的返回的数据类型并不是我们想要的输出。ModelLoader定义如下


public interface ModelLoader<Model, Data> {
 class LoadData<Data> {
     //数据加载的key
   public final Key sourceKey;
   public final List<Key> alternateKeys;
     //获取数据的接口,对应获取不同类型的数据实现
   public final DataFetcher<Data> fetcher;

   public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
     this(sourceKey, Collections.<Key>emptyList(), fetcher);
  }

   public LoadData(@NonNull Key sourceKey, @NonNull List<Key> alternateKeys,
       @NonNull DataFetcher<Data> fetcher) {
     this.sourceKey = Preconditions.checkNotNull(sourceKey);
     this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
     this.fetcher = Preconditions.checkNotNull(fetcher);
  }
}

   //创建LoadData 对象
 @Nullable
 LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,
     @NonNull Options options);
   //判断当前的ModelLoader是否能够处理这个model
 boolean handles(@NonNull Model model);
}

DataFetcher: 用于进行数据加载,不同的类型有不同的DataFetcher


SourceGenerator远程数据加载过程


@Override
public boolean startNext() {
 //...
 boolean started = false;
 while (!started && hasNextModelLoader()) {
   loadData = helper.getLoadData().get(loadDataListIndex++);
   if (loadData != null
       && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
       || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
     started = true;
     loadData.fetcher.loadData(helper.getPriority(), this);
  }
}
 return started;
}

代码流程:


通过DecodeHelper获取LoadData,遍历每一个LoadData;


查看当前LoadData加载出来的数据能不能,转换成我们想要的输出数据,如果可以的话就是用当前loadData进行数据加载。


DecodeHelpe#getLoadData()


List<LoadData<?>> getLoadData() {
 if (!isLoadDataSet) {
   isLoadDataSet = true;
   loadData.clear();
     //此处的model就是我们 通过调用load传递进来的参数 即输入
   List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
   //noinspection ForLoopReplaceableByForEach to improve perf
   for (int i = 0, size = modelLoaders.size(); i < size; i++) {
     ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
     //通过modelLoader 构建loadData
     LoadData<?> current =
         modelLoader.buildLoadData(model, width, height, options);
     if (current != null) {
       loadData.add(current);
    }
  }
}
 return loadData;
}

ModelLoaderRegistry#getModelLoaders


getModelLoaders()实现的位置在ModelLoaderRegistry#getModelLoaders


public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
   //获取对应的modelLoader
 List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
 int size = modelLoaders.size();
 boolean isEmpty = true;
 List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
 //noinspection ForLoopReplaceableByForEach to improve perf
 for (int i = 0; i < size; i++) {
   ModelLoader<A, ?> loader = modelLoaders.get(i);
     //判断对应的modelLoader是否有能力处理对应的model
   if (loader.handles(model)) {
     if (isEmpty) {
       filteredLoaders = new ArrayList<>(size - i);
       isEmpty = false;
    }
     filteredLoaders.add(loader);
  }
}
 return filteredLoaders;
}

getModelLoadersForClass主要是通过MultiModelLoaderFactory#build。然后MultiModelLoaderFactory会遍历所有已经注册的ModelLoader,只要当前的model是已经注册model的子类或者对应的实现,那么就会把对应的ModelLoader添加到待返回的集合中。


DecodeHelper#hasLoadPath


boolean hasLoadPath(Class<?> dataClass) {
   return getLoadPath(dataClass) != null;
}

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

可以看到hasLoadPath代码其实非常简单,就是获取一个LoadPath集合。获取的时候传递了三个参数 DataFetcher加载出来的数据类型dataClass,resourceClass ,transcodeClass


getLoadPath参数


对于resourceClass ,transcodeClass在DecodeHelper定义如下:


private Class<?> resourceClass;
private Class<Transcode> transcodeClass;

他们在init方法中进行初始化,经过层层代码的流转我们发现最终的参数初始化来自于RequestBuilder#obtainRequest


private Request obtainRequest(
     Target<TranscodeType> target,
     RequestListener<TranscodeType> targetListener,
     BaseRequestOptions<?> requestOptions,
     RequestCoordinator requestCoordinator,
     TransitionOptions<?, ? super TranscodeType> transitionOptions,
     Priority priority,
     int overrideWidth,
     int overrideHeight,
     Executor callbackExecutor) {
   return SingleRequest.obtain(
       context,
       glideContext,
       model,
       //该参数会在调用as系列方法后初始化,指向的是我们想要的输出类型。
       transcodeClass,
       //指向的是RequestBuilder 自身
       requestOptions,
       overrideWidth,
       overrideHeight,
       priority,
       target,
       targetListener,
       requestListeners,
       requestCoordinator,
       glideContext.getEngine(),
       transitionOptions.getTransitionFactory(),
       callbackExecutor);
}

而RequestOptions#getResourceClass返回的resourceClass默认情况下返回的是Object,而在asBitmap和asGifDrawable会做其它的转换。


private static final RequestOptions DECODE_TYPE_BITMAP = decodeTypeOf(Bitmap.class).lock();
private static final RequestOptions DECODE_TYPE_GIF = decodeTypeOf(GifDrawable.class).lock();

@NonNull
 @CheckResult
 public RequestBuilder<Bitmap> asBitmap() {
   return as(Bitmap.class).apply(DECODE_TYPE_BITMAP);
}
 public RequestBuilder<GifDrawable> asGif() {
   return as(GifDrawable.class).apply(DECODE_TYPE_GIF);
}

getLoadPath执行过程


getLoadPath最终会调用Registry#getLoadPath


@Nullable
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
   @NonNull Class<Data> dataClass, @NonNull Class<TResource> resourceClass,
   @NonNull Class<Transcode> transcodeClass) {
   //先获取DecodePath  
 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;
}

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的解码器
   List<Class<TResource>> registeredResourceClasses =
       decoderRegistry.getResourceClasses(dataClass, resourceClass);
   for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
       //获取能够解析当前输入dataClass且将数据转变成我们想要的transcodeClass类型的转换类
     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;
}

整个过程涉及到两个关键类LoadPath DecodePath。


LoadPath 由数据类型datacalss 和 DecodePath组成


DecodePath 由数据类型dataclass 解码器 ResourceDecoder 集合 和资源转换 ResourceTranscoder 构成。总体上而言 一个LoadPath的存在代表着可能存在一条路径能够将ModelLoader加载出来的data解码转换成我们指定的数据类型。


DocodeJob数据解码的过程


Glide DecodeJob 的工作过程我们知道SourceGenerator在数据加载完成之后如果允许缓存原始数据会再次执行SourceGenerator#startNext将加载的数据进行缓存,然后通过DataCacheGenerator从缓存文件中获取。最终获取数据成功后会调用DocodeJob#onDataFetcherReady


作者:小小小小小鹿
链接:https://juejin.cn/post/7036389645168410655
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册