Glide鉴赏

698 阅读5分钟

Glide介绍

Glide是google官方推荐的Android图片加载库,注重让图片加载变的流畅。支持加载视频快照(将本地视频文件地址传入,则展示该视频的快照),普通图片(png,jpg)和gif图片。内部默认采用HttpUrlConnection,也支持集成Volley或OkHttp。它还具有以下一些优良特性:

  • 使用简单,API采用链式调用
  • 会绑定页面生命周期,动态管理图片加载请求
  • 灵活配置图片缓存策略
  • 防止频繁主线程I/O,垃圾回收而导致的页面闪烁卡顿

基本功能

设置加载选项(Option)

包括设置请求选项RequestOption(占位图,转换,缓存策略),过渡选项TransitionOption,缩略图,加载失败图,设置加载超时时间等

变换(Transformation)

支持裁剪,转换为gif,自定义BitmapTransformation,对于target为imageView则会自动按照View的scaleType变换

过渡(Transition)

在内存缓存中加载图像不会有过渡效果,在磁盘缓存,本地源文件,网络url等加载才有过渡效果

配置

添加AppGlideModdule或LibraryGlideModule实现,注册定制的组件(如ModelLoader),控制 Glide 的内存和磁盘缓存使用(大小,存储位置,策略),添加默认请求选项

缓存

Glide采用多级缓存策略

  • Actiive Resource:是否在另一个View正在显示该图片
  • Memory cache:是否最近加载过且还在内存中
  • Resource:是否之前被解码,转换并写入到磁盘缓存
  • Data:构建的资源是否被写入过文件缓存

若上述都没有则返回到原始资源取数据(原始文件,Uri,url)

缓存的键包含请求的model和一些其他数据,(高度,宽度,option等,因此只要对应的任意一个配置变了,都会生成一个新的文件),以及可选的签名(可用于定制缓存刷新策略,如同一个url版本的控制)。我们无法删除特定url或文件路径下对应的缓存文件,因此只能通过增加特定标识(签名版本)让之前的请求的model失效,达到刷新缓存的目的。也可以调用clear方法清理所有缓存

资源重用

Glide采用引用计数法追踪和重用资源

Glide全面分析

Glide架构

Model

要加载的数据最初来源,图片的数据来源可以是url,资源id,文件等,也v可以是GlideUrl等封装的对象,这些数据的来源通过load方法传入,可以参考ModelTypes接口

interface ModelTypes<T> {
  @NonNull
  @CheckResult
  T load(@Nullable Bitmap bitmap);
  // 省略其他load方法
}

ModelLoader和LoadData

将Model封装成LoadData,上面的Model不能直接解析为图片,只有经过ModelLoader的buildLoadData方法生成成Data后,才能进一步被解析成图片,在LoadData中有表示数据唯一性的key,以及用于加载图片数据的DataFetcher(HttpUrlFetcher, OkHttpStreamFetcher,FilePathFetcher等),DataFetcher的loadData方法则会真正执行加载数据,如OkHttpStreamFetcher执行网络请求得到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);
    }
  }
  @Nullable
  LoadData<Data> buildLoadData(
      @NonNull Model model, int width, int height, @NonNull Options options);

  boolean handles(@NonNull Model model);
}

ResourceDecoder

用来将DataFetcher里得到的数据(如InputStream)解析成Resource(内部封装Bitmap)。通过上面的DataFetcher的loaddata得到了真正可以加载图片的数据后,会经过DecodeJob对这些数据封装成Resource。接着前面分析到从OkHttpStreamFetcher执行网络请求得到InputStream,则data.getClass()则为InputStream.class,通过Registry的getLoadPath方法得到对应的加载路径LoadPath,在加载路径里得到对应的解码路径DecodePath,从而得到所有解码器ResourceDecoder,执行ResourceDecoder的decode方法,最终将数据解码为BitmapResource。

class DecodeJob {
  private void decodeFromRetrievedData() {
    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();
    }
  }

  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);
  }
}
public interface ResourceDecoder<T, Z> {
  
  boolean handles(@NonNull T source, @NonNull Options options) throws IOException;
  
  @Nullable
  Resource<Z> decode(@NonNull T source, int width, int height, @NonNull Options options)
      throws IOException;
}

Resource

Resource是对要加载的资源(Bitmap,BitmapDrawable等)进行包装,通过对象池技术进行缓存和复用。以BitmapResourc为例,采用BitmapPool对Bitmap资源进行包装,封装获取Bitmap等方法。

public class BitmapResource implements Resource<Bitmap>, Initializable {
  private final Bitmap bitmap;
  private final BitmapPool bitmapPool;

  @Nullable
  public static BitmapResource obtain(@Nullable Bitmap bitmap, @NonNull BitmapPool bitmapPool) {
    if (bitmap == null) {
      return null;
    } else {
      return new BitmapResource(bitmap, bitmapPool);
    }
  }

  public BitmapResource(@NonNull Bitmap bitmap, @NonNull BitmapPool bitmapPool) {
    this.bitmap = Preconditions.checkNotNull(bitmap, "Bitmap must not be null");
    this.bitmapPool = Preconditions.checkNotNull(bitmapPool, "BitmapPool must not be null");
  }![Glide鉴赏](/assets/Glide鉴赏.md)

  @NonNull
  @Override
  public Class<Bitmap> getResourceClass() {
    return Bitmap.class;
  }

  @NonNull
  @Override
  public Bitmap get() {
    return bitmap;
  }

  @Override
  public int getSize() {
    return Util.getBitmapByteSize(bitmap);
  }

  @Override
  public void recycle() {
    bitmapPool.put(bitmap);
  }

  @Override
  public void initialize() {
    bitmap.prepareToDraw();
  }
}

Encoder和ResourceEncoder

Encoder用来持久化上面DataFetcher里获取到的数据(如InputStream,ByteBuffer),而ResourceEncoder则是用来持久化上面Resource里的数据(如Bitmap,BitmapDrawable,GifDrawable)

Transformation

对加载得到的Resource进行变换成新的Resource,如圆角图片,圆形图片等

ResourceTranscoder

对Transformation得到对Resource,根据Options选项进一步转换

Target

加载图片最终要处理对对象,比如封装将图片设置到ImageView到系列接口,监听生命周期等,设置加载图片请求和取消请求

Glide加载流程

一个简单的加载

Glide.get 分析

Glide.with 分析

RequestManager#load(Object model) 分析

RequestBuilder#into(T target) 分析

以SimpleRequest为例分析Request#begin方法

DecodeJob#run方法分析

得到图片数据后,下一步会进一步封装

参考资料

Glide源码导读

glide架构描述

主流开源框架之Glide深入了解

Glide 4.9源码解析-图片加载流程