Android 图片加载库Glide知其然知其所以然之加载

栏目: IOS · Android · 发布时间: 5年前

内容简介:距上篇没看过上篇的小伙伴可以看下

距上篇 Glide 分析的文章已经一个月了,实际分析起来Glide很复杂,对这种复杂进行形容的话,那就是“成吨的复杂”一点也不为过。

没看过上篇的小伙伴可以看下

Android图片加载库Glide 知其然知其所以然 开篇

Android 图片加载库Glide知其然知其所以然之加载
这次我从Glide的创建开始谈起,然后着重聊下, DataLoader

这个环节,在此之前我们先搞明白,Glide是怎样把图片加载这个过程拆分解耦的。先说结论他的加加载主要分为这样几个过程:

  • 加载数据:Model转Data
    将复杂不可确定的Model转换成能够直接编码处理的Data,这里对Model包括String Uri URL等就是我们使用Glide.with(this).load(xxx)里面的xxx;Data表示的是能够被编码器识别的数据,包括byte[] 字节数组,InputStream字节流,File文件等等。
  • 编码:Data转Resource 这个就是将url String file等通过网络加载文件读取等方式转换最终加载成文件,Bitmap,Drawable,Gif等的过程。
  • 转码:Resource 转Resouce 获取到的资源直接能够灵活的转换,包括bitmap转成字节数组,字节数组转成bitmap转Drawable,gif转字节数组等。

去做Model转Data 、Data转Resource、Resource互转工作的类分别是一次分别是ModelLoader<Model, Data> 、ResourceDecoder<T, Z>以及ResourceTranscoder<Z, R> 接口的实现。

另外有了解码也就是从文件、url、等解析成资源的过程也会设计到将Resource转成Flile文件的过程,做这个工作的interface Encoder 接口的实现类。

想必大家都听说过“高内聚,低耦合”,“上层不应该依赖于底层而是应该依赖与抽象”这样的说法。 Glide把刚刚上面我描述的这些功能采用了一种注册机制进行架构。在Glide的这个类构造方法里有这些的代码

Glide(...){
 registry
        //将byteBuffer转换为Flile
        .append(ByteBuffer.class, new ByteBufferEncoder())
        //将inputStream转换为File
        .append(InputStream.class, new StreamEncoder(arrayPool))
        /* Bitmaps */
        
        //ByteBuffer解码成Bitmap :decoderRegistry
        .append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
        ....
        /* BitmapDrawables */
        .append(
            Registry.BUCKET_BITMAP_DRAWABLE,
            ByteBuffer.class,
            BitmapDrawable.class,
            new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
        /* GIFs */
        .append(
            Registry.BUCKET_GIF,
            InputStream.class,
            GifDrawable.class,
            new StreamGifDecoder(imageHeaderParsers, byteBufferGifDecoder, arrayPool))
        /* Files */
        .append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
     FileLoader.FileDescriptorFactory())
        // Compilation with Gradle requires the type to be specified for UnitModelLoader here.
        .append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
        /* Models */
        .register(new InputStreamRewinder.Factory(arrayPool))
        .append(int.class, InputStream.class, resourceLoaderStreamFactory)
        .append(
            int.class,
            ParcelFileDescriptor.class,
            resourceLoaderFileDescriptorFactory)
        .append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
...
        /* Transcoders */
        .register(
            Bitmap.class,
            BitmapDrawable.class,
            new BitmapDrawableTranscoder(resources))
        .register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
        .register(
            Drawable.class,
            byte[].class,
            new DrawableBytesTranscoder(
                bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
        .register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
}
复制代码

代码实在太长我删减的一部分,总体来讲,他就是往里注册了ModuleLoader、Encoder、Decoder、Transcoder这四种组件,使其具备转换模型数据,解析String url等源,解析加载bitmap drawable file用的Decoder,对资源进行转码的Transcoder,以及将资源转成flile的Encoder。这些功能单元都被注册在了Registry,这个类的注释是这样写的,这其中的架构思想大家自行发散思考。

Manages component registration to extend or replace Glide's default loading, decoding, and encoding logic.

言归正传,这篇我们着重分析的就是Loading这个过程,抽象的讲就是把复杂的Model(String Url 等)转换成 Data(byte[] InputStream)过程。上面我们知道了Loading在Glide整个系统中的位置,他的实现是怎样的呢。

一、ModelLoader

作用将复杂多变抽象的Mode 如String URL 数据转换为直接被编码的数据如byte[] inputStream等。

具体实现

public interface ModelLoader<Model, Data> {

  class LoadData<Data> {
    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);
    }
  }

  /** 给定model 宽高信息 已经Options生成LoadData<?> **/
  @Nullable
  LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,
      @NonNull Options options);
  boolean handles(@NonNull Model model);
}

复制代码

生产LoadDate的地方,LoadData里面有个成员变量DataFetcher<>

二、 DataFetcher

这个类的对象是加载数据的实际负责人。

public interface DataFetcher<T> {

  interface DataCallback<T> {
    void onDataReady(@Nullable T data);
    void onLoadFailed(@NonNull Exception e);
  }
  //加载资源文件,这些资源文件可能是字节数组,文件等,都是用现在抽象T代表
  void loadData(@NonNull Priority priority, @NonNull DataCallback<? super T> callback);
  
  void cleanup();
  
  void cancel();

  @NonNull
  Class<T> getDataClass();

  @NonNull
  DataSource getDataSource();
}

复制代码

DataFetcher是一个泛型类,泛型T表示的就是要加载的Data类型。他的功能实现形式是通过回调的,回调接口DataCallback,DataCallback定义了两个方法,onDataReady(T data)和onLoadFailed(Exception e); 见名知意,DataFetcher是获取数据的抽象,他规定了获取Data的时候是通过调用loadData这个方法,这个方法其中有一个参数是DataCallback,当Data获取成功会调用callBack的onDataReady,当获取失败的使时候会调用onLoadFailed(Exception e)通知外界。

三、 ModelLoader的生产者,ModelLoaderFactory<T,Y>

我们在往Registry注册Loader的时候,可以清晰的看到其实往里添加的主要是三个内容Class Class 以及ModelLoaderFactory。没错ModelLoaderFactory就是构建ModelLoder的工厂类,前两个参数指明了被处理的数据类型和加载成的数据类型。

.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
复制代码

ModelLoaderFactory的代码。

public interface ModelLoaderFactory<T, Y> {
  ModelLoader<T, Y> build(@NonNull MultiModelLoaderFactory multiFactory);
  void teardown();
}
复制代码

值得注意是是在build方法中传入了MultiModelLoaderFactory一看名字就不简单啊,“多种模型加载工厂”,不贴代码了真相是往Registry对象中注册的ModelLoaderFactory最终都是保存在MultiModelLoaderFactory中的。

四、 DataFetcher生产者 DataFetcherGenerator

interface DataFetcherGenerator {

  interface FetcherReadyCallback {
    void reschedule();
    
    void onDataFetcherReady(
        Key sourceKey, @Nullable Object data, 
        DataFetcher<?> fetcher,DataSource dataSource, 
        Key attemptedKey);

    void onDataFetcherFailed(Key attemptedKey, Exception e,
        DataFetcher<?> fetcher,DataSource dataSource);
  }
  boolean startNext();

  void cancel();
}

复制代码
  • startNext()方法就是获取一个DataFetcher 并且开始执行获取Data操作,如果开始执行了返回true,否则返回false;
  • cancle()取消获取
  • FetcherReadyCallback接口供调用者,onDataFetcherReady和onDataFetcherFailed()两个方法见名知意不赘述,值得注意的是onDataFetcherReady()方法有个 Object data参数,他的含义是要 能直接编码的数据Data,这就意味着,当onDataFetcherReady()方法被回调的时候,不但已经穿件了DataFetcher对象,且DataFetcher对象已经获取到了数据Data;
  • reschedule()的方法调用它能起的作用是重新执行startNext()方法。

DataFetcherGenerator的集成类有三个

  • SourceGenerator (从数据源中获取数据,会设计缓存策略)
  • DataCacheGenerator(磁盘获取原始缓存数据)
  • ResourceCacheGenerator(磁盘中获取变化过的数据)

在已经注册号的Modlerloader中查找处理

  • 在DecodeHelper提供LoadData对象
DecodeHelper{
    ...
List<LoadData<?>> getLoadData() {
    if (!isLoadDataSet) {
      isLoadDataSet = true;
      loadData.clear();
      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);
        LoadData<?> current =
            modelLoader.buildLoadData(model, width, height, options);
        if (current != null) {
          loadData.add(current);
        }
      }
    }
return loadData;
}
    ...
}
复制代码

以SourceGenerater为例

startNext方法

@Override
  public boolean startNext() {
    //当dataToCache不为空的时候,将dataToCache放入内存中
    //然后然后使用sourceCahceGenerator获取Data
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;
    
    loadData = null;
    boolean started = false;
    //获取能够用的LoadData对象,然后调用它的LoadData()
    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;
  }

复制代码

SourceGenerator实现了DataCallBack接口

@Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // We might be being called back on someone else's thread. Before doing anything, we should
      // reschedule to get back onto Glide's thread.
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }
复制代码

获取到数据可以被缓存的情况下就会将data赋值给dataToCache,如果不能被赋就回调传入的FetcherReadyCallback对象的onReadyCallback方法。

Android 图片加载库Glide知其然知其所以然之加载

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

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

数学拾遗

数学拾遗

加黑蒂 / 清华大学出版社 / 2004-8 / 49.00元

Beginning graduate students in mathematics and other quantitative subjects are expected to have a daunting breadth of mathematical knowledge ,but few have such a backgroud .This book will help stedent......一起来看看 《数学拾遗》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具