Glide编解码流程

LoadData相关讲解

glide编码流程主要是通过ModelLoader、LoadData等类进行完成的,下面先介绍这几个类的类图关系:

alt text

ModelLoader 是用来构建LoadData,而LoadData中对应了不同的DataFetcher,DataFetcher是最终加载数据层。
在上一节中介绍过SourceGenerator在startNext方法中fetcher是HttpUrlFetcher,以及在获取到数据后,进行保存到磁盘,然后调用了DataCacheGenerator的startNext方法,然后在里面用到了ByteBufferFetcher,它们都是怎么来的呢?下面通过源码分析如何获取的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class SourceGenerator{
  public void startNext(){
    loadData = null;
    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;
        startNextLoad(loadData);
      }
    }
  }
}

在SourceGenerator的startNext方法中有上面代码,首先通过hasNextModelLoader方法判断有没有合适的LoadData:

1
2
3
private boolean hasNextModelLoader() {
  return loadDataListIndex < helper.getLoadData().size();
}

判断当前索引是否小于helper获取的loadData的数量,helper是DecodeHelper辅助类,它是在DecodeJob中初始化的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
List<LoadData<?>> getLoadData() {
  if (!isLoadDataSet) {
    isLoadDataSet = true;
    loadData.clear();
    List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
    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;
}

glideContext是glide统一的上下文,它继承自ContextWrapper,是在Glide对象初始化的时候创建的,接着调用了getRegistry方法,该方法的定义如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public final class GlideSuppliers {
  public interface GlideSupplier<T> {
    T get();
  }

  private GlideSuppliers() {}

  public static <T> GlideSupplier<T> memorize(final GlideSupplier<T> supplier) {
    return new GlideSupplier<T>() {
      private volatile T instance;

      @Override
      public T get() {
        if (instance == null) {
          synchronized (this) {
            if (instance == null) {
              instance = Preconditions.checkNotNull(supplier.get());
            }
          }
        }
        return instance;
      }
    };
  }
}
public class GlideContext{
  private final GlideSupplier<Registry> registry;
  public GlideContext(@NonNull Context context,@NonNull GlideSupplier<Registry> registry) {
    super(context.getApplicationContext());
    this.registry = GlideSuppliers.memorize(registry);
  }
  public Registry getRegistry() {
    return registry.get();
  }
}

getRegistry方法中是调用了GlideContext的registry变量的get方法,而registry变量是通过GlideSuppliers.memorize构建的,GlideSupplier是一个接口,定义了get方法,而在GlideSuppliers.memorize方法中返回的GlideSupplier对象中重写的get方法是通过memorize方法supplier参数的get来获取的。也就是说创建GlideSupplier对象交给了外界来创建。为什么在memorize方法中不直接返回supplier.get()呢?而需要在memorize包装一个匿名的GlideSupplier,这是因为在业务层多处调用GlideContext的getRegistry方法,如果在memorize方法中直接调用supplier.get(),那么每次返回新的Registry实例,所以在memorize方法中定义了一个匿名的GlideSupplier,在它的get方法里面通过双重加锁来控制单一的Registry实例。外界传进来的GlideSupplier是在创建Glide对象的时候构建的:

1
2
3
4
Glide(@NonNull Context context) {
    GlideSupplier<Registry> registry = RegistryFactory.lazilyCreateAndInitializeRegistry(this, manifestModules, annotationGeneratedModule);
    glideContext =new GlideContext(context,registry);
}

外界传进来的GlideSupplier是通过RegistryFactory.lazilyCreateAndInitializeRegistry来创建的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
static GlideSupplier<Registry> lazilyCreateAndInitializeRegistry(){
  return createAndInitRegistry(glide, manifestModules, annotationGeneratedModule);
}

static Registry createAndInitRegistry(
    Glide glide,
    List<GlideModule> manifestModules,
    @Nullable AppGlideModule annotationGeneratedModule) {
  BitmapPool bitmapPool = glide.getBitmapPool();
  ArrayPool arrayPool = glide.getArrayPool();
  Context context = glide.getGlideContext().getApplicationContext();
  GlideExperiments experiments = glide.getGlideContext().getExperiments();
  Registry registry = new Registry();
  initializeDefaults(context, registry, bitmapPool, arrayPool, experiments);
  initializeModules(context, glide, registry, manifestModules, annotationGeneratedModule);
  return registry;
}

这样将Registry的初始化交给了RegistryFactory来创建,所有的配置收拢到一块,更加聚合。回到上面DecodeHelper的getLoadData方法,接着调用了Registry的getModelLoaders方法:

1
2
3
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
  return modelLoaderRegistry.getModelLoaders(model);
}

modelLoaderRegistry是在Registry中初始化的,看下它的getModelLoaders方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
  List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
  int size = modelLoaders.size();
  boolean isEmpty = true;
  List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
  for (int i = 0; i < size; i++) {
    ModelLoader<A, ?> loader = modelLoaders.get(i);
    if (loader.handles(model)) {
      if (isEmpty) {
        filteredLoaders = new ArrayList<>(size - i);
        isEmpty = false;
      }
      filteredLoaders.add(loader);
    }
  }
  return filteredLoaders;
}

private synchronized <A> List<ModelLoader<A, ?>> getModelLoadersForClass(
    @NonNull Class<A> modelClass) {
  List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
  if (loaders == null) {
    loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
    cache.put(modelClass, loaders);
  }
  return loaders;
}

在getModelLoaders通过model的Class类型,调用了getModelLoadersForClass方法,在该方法里面从cache里面取拿ModelLoader集合,它是包装map的ModelLoaderCache类,首次为空,接着调用了multiModelLoaderFactory.build(modelClass)把loaders放到cache中。注意下,该方法中使用Collections.unmodifiableList包装了一层集合,该集合不允许进行添加元素,主要是为了不让后续添加新的ModelLoader,防止在边遍历的时候会有其他地方添加元素而产生ConcurrentModificationException异常。并且此处使用了cache缓存,下次直接从该map中取该model下的ModelLoader集合,无需再次进行构建。看下该方法怎么获取loaders的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
  try {
    List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
    for (Entry<?, ?> entry : entries) {
      if (alreadyUsedEntries.contains(entry)) {
        continue;
      }
      if (entry.handles(modelClass)) {
        alreadyUsedEntries.add(entry);
        loaders.add(this.<Model, Object>build(entry));
        alreadyUsedEntries.remove(entry);
      }
    }
    return loaders;
  } catch (Throwable t) {
    alreadyUsedEntries.clear();
    throw t;
  }
}

entries是Entry对象的集合,注意了上面使用了alreadyUsedEntries集合标记Entry有没有被构建,比如在构建a这个modelLoader中又去构建b这个modelLoader,而在b中又去构建a,这就导致循环构建,此时通过该集合能解决这个循环依赖问题。Entry对象里面存储了modelClass、dataClass、modelLoaderFactory:

1
2
3
4
5
private static class Entry<Model, Data> {
  private final Class<Model> modelClass;
  @Synthetic final Class<Data> dataClass;
  @Synthetic final ModelLoaderFactory<? extends Model, ? extends Data> factory;
}

而entries集合中的Entry元素是在上面分析Registry对象创建过程中的RegistryFactory中添加进来的:

1
2
3
4
5
6
7
8
9
public class RegistryFactory{
  private static void initializeDefaults(Registry registry){
  registry
        .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
        .append(String.class, InputStream.class, new StringLoader.StreamFactory())
        .append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
        .append(String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
  }
}

append方法三个参数对应分别对应了上面Entry对象的modelClass、dataClass、ModelLoaderFactory,所以在上面multiModelLoaderFactory的build方法中遍历entries时候,通过entry的handles(modelClass)来进行过滤:

1
2
3
public boolean handles(@NonNull Class<?> modelClass) {
  return this.modelClass.isAssignableFrom(modelClass);
}

如果传进来的是String类型,则会过滤出上面4个通过append方法构建的Entry对象,接着会调用multiModelLoaderFactory的build(entry)方法:

1
2
3
private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
  return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
}

通过entry的factory的build方法创建ModelLoader,上面4个entry的factory分别是 DataUrlLoader.StreamFactoryStringLoader.StreamFactoryStringLoader.FileDescriptorFactoryStringLoader.AssetFileDescriptorFactory ,分别调用它们的build方法创建不同的ModelLoader:

1
2
3
public ModelLoader<Model, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
  return new DataUrlLoader<>(opener);
}

所以返回了4个ModelLoader分别是DataUrlLoader、StringLoader、StringLoader、StringLoader。继续回到ModelLoaderRegistry的getModelLoaders方法,接着通过ModelLoader的handles(modelClass)来过滤,DataUrlLoader的handles方法如下:

1
2
3
4
5
private static final String DATA_SCHEME_IMAGE = "data:image";
@Override
public boolean handles(@NonNull Model model) {
  return model.toString().startsWith(DATA_SCHEME_IMAGE);
}

DataUrlLoader中必须要求model是以 data:image 开头才行,而StringLoader的handles(modelClass)如下:

1
2
3
4
@Override
public boolean handles(@NonNull String model) {
  return true;
}

所以modelLoaderRegistry的getModelLoaders方法返回后面3个StringLoader了。继续回到DecodeJob的getLoadData方法,获取到3个ModelLoader后,继续调用它们的buildLoadData方法来获取LoadData,它们都是StringLoader,来看下它的buildLoadData方法:

1
2
3
4
5
6
7
8
9
@Override
public LoadData<Data> buildLoadData(
    @NonNull String model, int width, int height, @NonNull Options options) {
  Uri uri = parseUri(model);
  if (uri == null || !uriLoader.handles(uri)) {
    return null;
  }
  return uriLoader.buildLoadData(uri, width, height, options);
}

首先将model转化成uri,然后调用uriLoader.handles(uri)过滤,在StringLoader中的uriLoader是一个MultiModelLoader,它是在创建StringLoader的时候传进来的,比如第一个StringLoader创建如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
  @NonNull
  @Override
  public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
    return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
  }
}

public synchronized <Model, Data> ModelLoader<Model, Data> build(
    @NonNull Class<Model> modelClass, @NonNull Class<Data> dataClass) {
  try {
    List<ModelLoader<Model, Data>> loaders = new ArrayList<>();
    boolean ignoredAnyEntries = false;
    for (Entry<?, ?> entry : entries) {
      if (alreadyUsedEntries.contains(entry)) {
        ignoredAnyEntries = true;
        continue;
      }
      if (entry.handles(modelClass, dataClass)) {
        alreadyUsedEntries.add(entry);
        loaders.add(this.<Model, Data>build(entry));
        alreadyUsedEntries.remove(entry);
      }
    }
    if (loaders.size() > 1) {
      return factory.build(loaders, throwableListPool);
    } else if (loaders.size() == 1) {
      return loaders.get(0);
    } else {
      if (ignoredAnyEntries) {
        return emptyModelLoader();
      } else {
        throw new NoModelLoaderAvailableException(modelClass, dataClass);
      }
    }
  } catch (Throwable t) {
    alreadyUsedEntries.clear();
    throw t;
  }
}

public boolean handles(@NonNull Class<?> modelClass, @NonNull Class<?> dataClass) {
  return handles(modelClass) && this.dataClass.isAssignableFrom(dataClass);
}

static class Factory {
  @NonNull
  public <Model, Data> MultiModelLoader<Model, Data> build(
      @NonNull List<ModelLoader<Model, Data>> modelLoaders,
      @NonNull Pool<List<Throwable>> throwableListPool) {
    return new MultiModelLoader<>(modelLoaders, throwableListPool);
  }
}
  1. StringLoader:uriLoader = MultiModelLoader(modelLoaders=MultiModelLoader [DataUrlLoader, AssetUriLoader, MediaStoreImageThumbLoader, MediaStoreVideoThumbLoader, QMediaStoreUriLoader, UriLoader, UrlUriLoader, UrlUriLoader])

  2. StringLoader:uriLoader = MultiModelLoader(modelLoaders=MultiModelLoader [QMediaStoreUriLoader, UriLoader])

  3. StringLoader:uriLoader = MultiModelLoader(modelLoaders=MultiModelLoader [ResourceUriLoader, AssetUriLoader, UriLoader])

所以经过StringLoader的uriLoader(MultiModelLoader)的handles方法过滤:

1
2
3
4
5
6
7
8
9
@Override
public boolean handles(@NonNull Model model) {
  for (ModelLoader<Model, Data> modelLoader : modelLoaders) {
    if (modelLoader.handles(model)) {
      return true;
    }
  }
  return false;
}

遍历modelLoaders,然后调用每一个modelLoader的handles方法,最终只有第一个StringLoader通过,是因为UrlUriLoader的handles方法通过:

1
2
3
4
5
6
7
8
public class UrlUriLoader<Data> implements ModelLoader<Uri, Data> {
  private static final Set<String> SCHEMES =
      Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));
  @Override
  public boolean handles(@NonNull Uri uri) {
    return SCHEMES.contains(uri.getScheme());
  }    
}

继续回到StringLoader的buildLoadData方法,接着会调用uriLoader.buildLoadData的方法,也就是MultiModelLoader的方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public LoadData<Data> buildLoadData(
    @NonNull Model model, int width, int height, @NonNull Options options) {
  Key sourceKey = null;
  int size = modelLoaders.size();
  List<DataFetcher<Data>> fetchers = new ArrayList<>(size);
  for (int i = 0; i < size; i++) {
    ModelLoader<Model, Data> modelLoader = modelLoaders.get(i);
    if (modelLoader.handles(model)) {
      LoadData<Data> loadData = modelLoader.buildLoadData(model, width, height, options);
      if (loadData != null) {
        sourceKey = loadData.sourceKey;
        fetchers.add(loadData.fetcher);
      }
    }
  }
  return !fetchers.isEmpty() && sourceKey != null
      ? new LoadData<>(sourceKey, new MultiFetcher<>(fetchers, exceptionListPool))
      : null;
}

最终交给了UrlUriLoader的buildLoadData方法获取LoadData:

1
2
3
4
5
public LoadData<Data> buildLoadData(
    @NonNull Uri uri, int width, int height, @NonNull Options options) {
  GlideUrl glideUrl = new GlideUrl(uri.toString());
  return urlLoader.buildLoadData(glideUrl, width, height, options);
}

它又交给了urlLoader的buildLoadData方法获取LoadData,此处的urlLoader实际是HttpGlideUrlLoader,它是在UrlUriLoader创建的时候获取的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public LoadData<InputStream> buildLoadData(
    @NonNull GlideUrl model, int width, int height, @NonNull Options options) {
  GlideUrl url = model;
  if (modelCache != null) {
    url = modelCache.get(model, 0, 0);
    if (url == null) {
      modelCache.put(model, 0, 0, model);
      url = model;
    }
  }
  int timeout = options.get(TIMEOUT);
  return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}

所以最终的LoadData是此处创建的,fetcher是HttpUrlFetcher。

InputStream到文件

在前面分析加载流程的时候,讲过SourceGenerator获取到InputStream后,将该InputStream保存到本地文件中,这个过程涉及到一次编码,然后将文件转成byteBuffer后,完成bitmap的解码,最后将bitmap又进行一次编码,这一次是对缩放后的图片进行编码。我们先看下SourceGenerator中如何将InputStream保存到本地文件,首先看下SourceGenerator的startNext方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class SourceGenerator{
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      boolean isDataInCache = cacheData(data);
      if (!isDataInCache) {
        return true;
      }
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
  }
}

HttpUrlFetcher回调到SourceGenerator后,dataToCache就不为空了,所以会调用cacheData(InputStream)保存到本地磁盘:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private boolean cacheData(Object dataToCache) throws IOException {
  DataRewinder<Object> rewinder = helper.getRewinder(dataToCache);
  Object data = rewinder.rewindAndGet();
  Encoder<Object> encoder = helper.getSourceEncoder(data);
  DataCacheWriter<Object> writer = new DataCacheWriter<>(encoder, data, helper.getOptions());
  DataCacheKey newOriginalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
  DiskCache diskCache = helper.getDiskCache();
  diskCache.put(newOriginalKey, writer);

  if (diskCache.get(newOriginalKey) != null) {
    originalKey = newOriginalKey;
    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
    return true;
  }
  return false;
}

helper是DecodeHelper,调用了getRewinder方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class DecodeHelper{
  <T> DataRewinder<T> getRewinder(T data) {
    return glideContext.getRegistry().getRewinder(data);
  }
}
public class Registry{
  public <X> DataRewinder<X> getRewinder(@NonNull X data) {
    return dataRewinderRegistry.build(data);
  }
}

public class DataRewinderRegistry{
  private final Map<Class<?>, DataRewinder.Factory<?>> rewinders = new HashMap<>();
  public synchronized <T> DataRewinder<T> build(@NonNull T data) {
    DataRewinder.Factory<T> result = (DataRewinder.Factory<T>) rewinders.get(data.getClass());
    if (result == null) {
      for (DataRewinder.Factory<?> registeredFactory : rewinders.values()) {
        if (registeredFactory.getDataClass().isAssignableFrom(data.getClass())) {
          result = (DataRewinder.Factory<T>) registeredFactory;
          break;
        }
      }
    }
    if (result == null) {
      result = (DataRewinder.Factory<T>) DEFAULT_FACTORY;
    }
    return result.build(data);
  }
}

DataRewinder是一个接口,这里获取DataRewinder类似上面介绍的ModelLoader,上面是将Entry装载到MultiModelLoaderFactory中,而此处是将 DataRewinder.Factory 放到DataRewinderRegistry中,通过比较class类型得到DataRewinder,最终得到一个 InputStreamRewinder ,接着调用了rewindAndGet方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public final class InputStreamRewinder implements DataRewinder<InputStream> {
  // 5MB.
  private static final int MARK_READ_LIMIT = 5 * 1024 * 1024;

  private final RecyclableBufferedInputStream bufferedStream;

  @Synthetic
  public InputStreamRewinder(InputStream is, ArrayPool byteArrayPool) {
    bufferedStream = new RecyclableBufferedInputStream(is, byteArrayPool);
    bufferedStream.mark(MARK_READ_LIMIT);
  }

  @NonNull
  @Override
  public InputStream rewindAndGet() throws IOException {
    bufferedStream.reset();
    return bufferedStream;
  }
}

在初始化InputStreamRewinder时候,将传进来的InputStream包装成RecyclableBufferedInputStream,相较于java中的BufferedInputStream,它是一个从byteArrayPool中申请byte[]数组的流,并且申请的默认byte[]数组大小是64KB,每次在扩容的时候,不是通过new byte[]的形式申请内存,而是通过byteArrayPool获取,申请到新的内存后会将旧的内存放到byteArrayPool中。基于以上特性能看出来,RecyclableBufferedInputStream是一个内存能循环利用的字节输入流,减少了GC。
接着通过DecodeHelper的getSourceEncoder方法获取到Encoder,跟上面获取DataRewinder是一样的,它是通过Registry到EncoderRegistry,然后也是比较传进来的class类型是不是注册的class子类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class EncoderRegistry {
  private final List<Entry<?>> encoders = new ArrayList<>();

  @SuppressWarnings("unchecked")
  @Nullable
  public synchronized <T> Encoder<T> getEncoder(@NonNull Class<T> dataClass) {
    for (Entry<?> entry : encoders) {
      if (entry.handles(dataClass)) {
        return (Encoder<T>) entry.encoder;
      }
    }
    return null;
  }

  private static final class Entry<T> {
    private final Class<T> dataClass;
    final Encoder<T> encoder;

    Entry(@NonNull Class<T> dataClass, @NonNull Encoder<T> encoder) {
      this.dataClass = dataClass;
      this.encoder = encoder;
    }

    boolean handles(@NonNull Class<?> dataClass) {
      return this.dataClass.isAssignableFrom(dataClass);
    }
  }
}

所以最终Encoder是一个StreamEncoder,回到上面的cacheData方法,DecodeHelper的getDiskCache方法会通过LazyDiskCacheProvider的getDiskCache方法返回DiskCache:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private static class LazyDiskCacheProvider implements DecodeJob.DiskCacheProvider {
  private final DiskCache.Factory factory;
  private volatile DiskCache diskCache;
  @Override
  public DiskCache getDiskCache() {
    if (diskCache == null) {
      synchronized (this) {
        if (diskCache == null) {
          diskCache = factory.build();
        }
        if (diskCache == null) {
          diskCache = new DiskCacheAdapter();
        }
      }
    }
    return diskCache;
  }
}

从此处看先通过 DiskCache.Factory 的build方法创建DiskCache,如果没有创建成功, 则返回DiskCacheAdapter。此处的DiskCache. Factory是在GlideBuilder中初始化Glide时候初始化的,它是InternalCacheDiskCacheFactory,它是继承自DiskLruCacheFactory,看下它的build方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Override
public DiskCache build() {
  File cacheDir = cacheDirectoryGetter.getCacheDirectory();
  if (cacheDir == null) {
    return null;
  }
  if (cacheDir.isDirectory() || cacheDir.mkdirs()) {
    return DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
  }
  return null;
}

可以看到最终是通过DiskLruCacheWrapper的create方法来创建DiskCache:

1
2
3
public static DiskCache create(File directory, long maxSize) {
  return new DiskLruCacheWrapper(directory, maxSize);
}

所以上面cacheData中diskCache是一个DiskLruCacheWrapper对象。最终通过DiskLruCacheWrapper的put方法来保存到文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Override
public void put(Key key, Writer writer) {
  String safeKey = safeKeyGenerator.getSafeKey(key);
  DiskLruCache diskCache = getDiskCache();
  Value current = diskCache.get(safeKey);
  if (current != null) {
    return;
  }
  DiskLruCache.Editor editor = diskCache.edit(safeKey);
  if (editor == null) {
    throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
  }
  File file = editor.getFile(0);
  if (writer.write(file)) {
    editor.commit();
  }
  editor.abortUnlessCommitted();
}

首先通过GlideUrl获取唯一key,因为此处是保存原始文件,所以是url作为唯一key,如果能找到本地图片,则不进行保存。接着获取该文件的editor:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public Editor edit(String key) throws IOException {
  return edit(key, ANY_SEQUENCE_NUMBER);
}
private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
  checkNotClosed();
  Entry entry = lruEntries.get(key);
  if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null
      || entry.sequenceNumber != expectedSequenceNumber)) {
    return null; // Value is stale.
  }
  if (entry == null) {
    entry = new Entry(key);
    lruEntries.put(key, entry);
  } else if (entry.currentEditor != null) {
    return null; // Another edit is in progress.
  }
  Editor editor = new Editor(entry);
  entry.currentEditor = editor;
  // Flush the journal before creating files to prevent file leaks.
  journalWriter.append(DIRTY);
  journalWriter.append(' ');
  journalWriter.append(key);
  journalWriter.append('\n');
  flushWriter(journalWriter);
  return editor;
}

在上面edit方法中,判断期望的序列号如果不是默认序列号并且entry为空或者已经保存成功了则直接返回null。下面就是创建Editor和Entry的过程。创建完Editor后就是使用writer的write方法保存图片的输入流了。此处的writer是DataCacheWriter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class DataCacheWriter<DataType> implements DiskCache.Writer {
  private final Encoder<DataType> encoder;
  private final DataType data;
  private final Options options;

  DataCacheWriter(Encoder<DataType> encoder, DataType data, Options options) {
    this.encoder = encoder;
    this.data = data;
    this.options = options;
  }

  @Override
  public boolean write(@NonNull File file) {
    return encoder.encode(data, file, options);
  }
}

会调用encoder的encode方法,可以看出来这是编码的核心,上面分析过encoder是StreamEncoder:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public boolean encode(@NonNull InputStream data, @NonNull File file, @NonNull Options options) {
  byte[] buffer = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
  boolean success = false;
  OutputStream os = new FileOutputStream(file);
  int read;
  while ((read = data.read(buffer)) != -1) {
    os.write(buffer, 0, read);
  }
  os.close();
  success = true;
  byteArrayPool.put(buffer);
  return success;
}

这就是编码的核心了,通过从字节池子中获取一个临时的byte数组,每次从输入流中读取,读取后通过输出流将buffer数组写到文件中,写完后,并回收该byte数组。

解码过程

解码前:获取ByteBufferFetcher

保存原始图片到本地后,就是bitmap编码了,在之前的glide加载流程介绍过,回到SourceGenerator的startNext方法,调完cacheData后,就会调用DataCacheGenerator的startNext方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public boolean startNext() {
  if (dataToCache != null) {
  }
  if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
    return true;
  }
}

public boolean startNext() {
  while (modelLoaders == null || !hasNextModelLoader()) {
    sourceIdIndex++;
    if (sourceIdIndex >= cacheKeys.size()) {
      return false;
    }

    Key sourceId = cacheKeys.get(sourceIdIndex);
    Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
    cacheFile = helper.getDiskCache().get(originalKey);
    if (cacheFile != null) {
      this.sourceKey = sourceId;
      modelLoaders = helper.getModelLoaders(cacheFile);
      modelLoaderIndex = 0;
    }
  }

  loadData = null;
  boolean started = false;
  while (!started && hasNextModelLoader()) {
    ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
    loadData =
        modelLoader.buildLoadData(
            cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
    if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
      started = true;
      loadData.fetcher.loadData(helper.getPriority(), this);
    }
  }
  return started;
}

上面和前面介绍的HttpUrlFetcher获取是一样的过程,只不过此处传进去的model是File类型,能匹配到Model是File类型有如下几个:

1
2
3
4
append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
append(File.class, InputStream.class, new FileLoader.StreamFactory())
append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())

这几个ModelLoader的handles方法都返回true,接着调用ModelLoader的buildLoadData获取LoadData,ByteBufferFileLoader的buildLoadData:

1
2
3
4
5
6
public class ByteBufferFileLoader implements ModelLoader<File, ByteBuffer> {
  public LoadData<ByteBuffer> buildLoadData(
      @NonNull File file, int width, int height, @NonNull Options options) {
    return new LoadData<>(new ObjectKey(file), new ByteBufferFetcher(file));
  }
}

FileLoader的buildLoadData:

1
2
3
4
5
6
7
public class FileLoader<Data> implements ModelLoader<File, Data> {
  @Override
  public LoadData<Data> buildLoadData(
      @NonNull File model, int width, int height, @NonNull Options options) {
    return new LoadData<>(new ObjectKey(model), new FileFetcher<>(model, fileOpener));
  }
}

UnitModelLoader的buildLoadData:

1
2
3
4
5
6
public class UnitModelLoader<Model> implements ModelLoader<Model, Model> {
  public LoadData<Model> buildLoadData(
      @NonNull Model model, int width, int height, @NonNull Options options) {
    return new LoadData<>(new ObjectKey(model), new UnitFetcher<>(model));
  }
}

回到前面DataCacheGenerator的startNext方法,获取到LoadData后,通过DecodeHelper的hasLoadPath是否为true:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
boolean hasLoadPath(Class<?> dataClass) {
  return getLoadPath(dataClass) != null;
}
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
  return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}

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);
  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;
}

通过getDecodePaths获取到已注册的DecodePath:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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;
}

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;
}

到这里后,我们需要熟悉bucketPriorityList和decoders,bucketPriorityList是分组信息,decoders是在Registry初始化注册进来的一些Entry集合表。在上面传进来的dataClass是ModelLoaders中创建的LoadData的fetcher中指定的getDataClass,resourceClass默认是Object.class:

alt text

alt text

从上面debug数据可以看出来,传进来的dataClass是 java.nio.ByteBuffer ,resourceClass是 java.lang.Object ,因为在拿到第一个ByteBufferFileLoader创建的LoadData中所创建的Fetcher是ByteBufferFetcher,它的getDataClass方法如下:

1
2
3
4
5
6
private static final class ByteBufferFetcher implements DataFetcher<ByteBuffer> {
  @Override
  public Class<ByteBuffer> getDataClass() {
    return ByteBuffer.class;
  }
}

通过上面的getResourceClasses中entry的handle(dataClass, resourceClass)进行过滤出dataClass为 java.nio.ByteBuffer 的Entry来,所以会返回 android.graphics.drawable.Drawablecom.bumptech.glide.load.resource.gif.GifDrawableandroid.graphics.drawable.Bitmapandroid.graphics.drawable.BitmapDrawable 这四种resourceClass。回到上面的getDecodePaths方法,registeredResourceClasses指向这四种class,接着遍历该集合。调用transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass),registeredTranscodeClasses会获取到对应resourceClass的TranscodeClasses,然后继续遍历registeredTranscodeClasses,获取每一个resourceClass的decoders,最后构建DecodePath添加到集合中返回,这里列出4种ResourceClass的DecodePath情况:

  • android.graphics.drawable.Drawable : DecodePath(dataClass= java.nio.ByteBuffer , registeredResourceClass= android.graphics.drawable.Drawable , registeredTranscodeClass= android.graphics.drawable.Drawable , decoders=[AnimatedImageDecoder$ByteBufferAnimatedImageDecoder、ByteBufferGifDecoder、BitmapDrawableDecoder、BitmapDrawableDecoder], transcoder=UnitTranscoder)
  • com.bumptech.glide.load.resource.gif.GifDrawable : DecodePath(dataClass= java.nio.ByteBuffer , registeredResourceClass= com.bumptech.glide.load.resource.gif.GifDrawable , registeredTranscodeClass= ByteBufferGifDecoder], transcoder=UnitTranscoder)
  • android.graphics.drawable.Bitmap : DecodePath(dataClass= java.nio.ByteBuffer , registeredResourceClass= android.graphics.drawable.Bitmap , registeredTranscodeClass= ByteBufferBitmapDecoder], transcoder=UnitTranscoder)
  • android.graphics.drawable.BitmapDrawable : DecodePath(dataClass= java.nio.ByteBuffer , registeredResourceClass= android.graphics.drawable.BitmapDrawable , registeredTranscodeClass= BitmapDrawableDecoderBitmapDrawableDecoder], transcoder=UnitTranscoder) 最终在Registry的getLoadPath方法中将返回的decodePaths构建成LoadPath,并添加到loadPathCache中,所以在上面DataCacheGenerator中loadData.fetcher是一个ByteBufferFetcher。

解码前:将File转化成ByteBuffer

在解码前需要将File转化成ByteBuffer,而没有直接通过File转化成Bitmap,原因有如下几点:

  • Glide 解码框架是基于 “Data → ByteBuffer/InputStream → Resource” 的模型设计
  • File → BitmapFactory 解码是顺序 I/O,不能 random access
  • 需要 Rewind(重读)—— File 做不到,ByteBuffer 支持
  • 支持 GIF、WebP 等复杂格式,必须使用 ByteBuffer
  • Glide 要支持 Transformation(圆角、裁剪、缩放)等 decode 前后处理
  • Glide 的缓存结构使用的是字节缓存,不是文件缓存(除磁盘缓存) 来看下ByteBufferFetcher的loadData实现:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Override
public void loadData(
    @NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) {
  ByteBuffer result = ByteBufferUtil.fromFile(file);
  callback.onDataReady(result);
}

public static ByteBuffer fromFile(@NonNull File file) throws IOException {
  RandomAccessFile raf = null;
  FileChannel channel = null;
  long fileLength = file.length();
  raf = new RandomAccessFile(file, "r");
  channel = raf.getChannel();
  return channel.map(FileChannel.MapMode.READ_ONLY, 0, fileLength).load();
}

首先获取RandomAccessFile的FileChannel,让你使用 NIO(New I/O)高速文件通道,让你能对文件进行更快、更底层的操作,然后通过map方法将file内容映射到内存,从而不像普通io一样read/write那样系统调用,不需要用户态内核态拷贝,返回的MappedByteBuffer 是内存映射文件,对 ByteBuffer 的操作就是在直接修改文件内容。比如下面通过put方法给指定位置插入字符的事例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int insertPos = 3;
byte value = (byte) '9';
RandomAccessFile raf = new RandomAccessFile("./test.txt", "rw");
FileChannel channel = raf.getChannel();
long oldLen = channel.size();
long newLen = oldLen + 1;
// 1. 扩展文件长度
raf.setLength(newLen);
// 2. 重新 map 全文件区域
MappedByteBuffer buffer = channel.map(
        FileChannel.MapMode.READ_WRITE,
        0,
        newLen
);
// 3. 从后往前移动数据(避免覆盖)
for (long i = oldLen - 1; i >= insertPos; i--) {
    byte b = buffer.get((int) i);
    buffer.put((int) (i + 1), b);
}
// 4. 插入新的字节
buffer.put(insertPos, value);
channel.close();
raf.close();

开始解码

转化成ByteBuffer就回到了DecodeJob的decodeFromRetrievedData方法,这个在Glide加载图片流程中有讲到,在该方法里面调用了decodeFromData方法:

1
2
3
4
5
private <Data> Resource<R> decodeFromData(
    DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
  Resource<R> result = decodeFromFetcher(data, dataSource);
  return result;
}

此处的data是上面说到的ByteBuffer,dataSource是ByteBufferFetcher的getDataSource方法返回的DataSource. LOCAL。继续看decodeFromFetcher方法:

1
2
3
4
5
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获取部分,上面分析过loadPathCache会构建一个LoadPath,里面放了4个DataPath,loadPathCache的dataClass是 ByteBuffer.class ,resourceClass是 java.lang.Object.class ,transcodeClass是 Drawable.class ,result就是刚刚上面说的4个DataPath。接着调用runLoadPath方法:

1
2
3
4
5
6
7
8
private <Data, ResourceType> Resource<R> runLoadPath(
    Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)
    throws GlideException {
  Options options = getOptionsWithHardwareConfig(dataSource);
  DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
  return path.load(
        rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
}

此处的rewinder和上面InputStreamRewinder获取类似,只不过这里的data是一个ByteBuffer.class类型,它获取到的是ByteBufferRewinder对象,然后调用LoadPath的load方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public Resource<Transcode> load(
    DataRewinder<Data> rewinder,
    @NonNull Options options,
    int width,
    int height,
    DecodePath.DecodeCallback<ResourceType> decodeCallback)
    throws GlideException {
  return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
}

//LoadPath.loadWithExceptionList
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;
  for (int i = 0, size = decodePaths.size(); i < size; i++) {
    DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
    result = path.decode(rewinder, width, height, options, decodeCallback);
    if (result != null) {
      break;
    }
  }
  return result;
}

//DecodePath.decode
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);
}

//DecodePath.decodeResource
private Resource<ResourceType> decodeResource(
    DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options)
    throws GlideException {
  return decodeResourceWithList(rewinder, width, height, options, exceptions);
}

//DecodePath.decodeResourceWithList
private Resource<ResourceType> decodeResourceWithList(
    DataRewinder<DataType> rewinder,
    int width,
    int height,
    @NonNull Options options,
    List<Throwable> exceptions)
    throws GlideException {
  Resource<ResourceType> result = null;
  for (int i = 0, size = decoders.size(); i < size; i++) {
    ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
    DataType data = rewinder.rewindAndGet();
    if (decoder.handles(data, options)) {
      data = rewinder.rewindAndGet();
      result = decoder.decode(data, width, height, options);
    }
    if (result != null) {
      break;
    }
  }
  return result;
}

最终通过DecodePath的decode方法进行解码Bitmap,上面说过LoadPath中存在4种DecodePath,最终调用到DecodePath的decodeResourceWithList,该方法里面会调用前面会遍历DecodePath中的decoders集合,然后调用ResourceDecoder的decode方法,那么直接看前面分析过有哪些decoder,比如第一个decoder是AnimatedImageDecoder$ByteBufferAnimatedImageDecoder,首先来看下它的handles方法:

1
2
3
4
5
private final AnimatedImageDecoder delegate;
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options)
    throws IOException {
  return delegate.handles(source);
}

此处的delegate是AnimatedImageDecoder,看下它的handles方法:

1
2
3
boolean handles(ByteBuffer byteBuffer) throws IOException {
  return isHandled(ImageHeaderParserUtils.getType(imageHeaderParsers, byteBuffer));
}

ImageHeaderParserUtils的getType方法是获取图片是PNG、WEBP等格式的方法,它是通过ByteBuffer相应位置的字节进行判断,imageHeaderParsers是DefaultImageHeaderParser和ExifInterfaceImageHeaderParser的集合,关于这部分就不用展开说了,后面会单独说如何获取图片的格式。再来看下isHandled方法:

1
2
3
4
private boolean isHandled(ImageType imageType) {
  return imageType == ImageType.ANIMATED_WEBP
      || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && imageType == ImageType.ANIMATED_AVIF);
}

如果图片是带动画的webp或者是ANIMATED_AVIF格式才会使用该decoder进行解码。最终在BUCKET=“BitmapDrawable”,dataClass=ByteBuffer.class,resourceClass=BitmapDrawable.class, decoder=BitmapDrawableDecoder,它的handles和decode如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
private final ResourceDecoder<DataType, Bitmap> decoder;
@Override
public boolean handles(@NonNull DataType source, @NonNull Options options) throws IOException {
  return decoder.handles(source, options);
}
@Override
public Resource<BitmapDrawable> decode(
    @NonNull DataType source, int width, int height, @NonNull Options options)
    throws IOException {
  Resource<Bitmap> bitmapResource = decoder.decode(source, width, height, options);
  return LazyBitmapDrawableResource.obtain(resources, bitmapResource);
}

此处的decoder实际一个ByteBufferBitmapDecoder:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/** Decodes {@link android.graphics.Bitmap Bitmaps} from {@link java.nio.ByteBuffer ByteBuffers}. */
public class ByteBufferBitmapDecoder implements ResourceDecoder<ByteBuffer, Bitmap> {
  private final Downsampler downsampler;

  public ByteBufferBitmapDecoder(Downsampler downsampler) {
    this.downsampler = downsampler;
  }

  @Override
  public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) {
    return downsampler.handles(source);
  }

  @Override
  public Resource<Bitmap> decode(
      @NonNull ByteBuffer source, int width, int height, @NonNull Options options)
      throws IOException {
    return downsampler.decode(source, width, height, options);
  }
}

类的注释也写得很清楚,Bitmap from ByteBuffers。
我们来看下Downsampler的handles和decode方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public boolean handles(@SuppressWarnings("unused") ByteBuffer byteBuffer) {
  // We expect downsampler to handle any available type Android supports.
  return true;
}

public Resource<Bitmap> decode(
    ByteBuffer buffer, int requestedWidth, int requestedHeight, Options options)
    throws IOException {
  return decode(
      new ImageReader.ByteBufferReader(buffer, parsers, byteArrayPool),
      requestedWidth,
      requestedHeight,
      options,
      EMPTY_CALLBACKS);
}

接着调用另外一个decode方法,注意此时创建的ImageReader是ByteBufferReader对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private Resource<Bitmap> decode(
    ImageReader imageReader,
    int requestedWidth,
    int requestedHeight,
    Options options,
    DecodeCallbacks callbacks)
    throws IOException {
  //从字节池子里面获取默认大小的字节数组
  byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
  //从池子里面获取Options
  BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
  //设置一个临时可选的缓冲区(byte[]),用于解码 Bitmap 时作为 中间读写缓冲
  bitmapFactoryOptions.inTempStorage = bytesForOptions;

  DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
  PreferredColorSpace preferredColorSpace = options.get(PREFERRED_COLOR_SPACE);
  DownsampleStrategy downsampleStrategy = options.get(DownsampleStrategy.OPTION);
  boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
  boolean isHardwareConfigAllowed =
      options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG);

  try {
    Bitmap result =
        decodeFromWrappedStreams(
            imageReader,
            bitmapFactoryOptions,
            downsampleStrategy,
            decodeFormat,
            preferredColorSpace,
            isHardwareConfigAllowed,
            requestedWidth,
            requestedHeight,
            fixBitmapToRequestedDimensions,
            callbacks);
    return BitmapResource.obtain(result, bitmapPool);
  } finally {
    releaseOptions(bitmapFactoryOptions);
    byteArrayPool.put(bytesForOptions);
  }
}

首先从池子中创建一个64KB的字节数组,然后从池子中获取一个默认的Options,接着将前面获取到的byte数组给到Options的inTempStorage,它的作用是给一个临时的缓冲区,用于解码bitmap时作为中间读写缓冲,如果不设置,系统每次解码时会自己new一个临时byte[]来用,该缓冲区主要在以下场景会用到:

  • 读取图片头(Header)
    • 比如检测是否 PNG/JPEG/WebP、宽高、是否有 alpha 等。
  • 读取压缩数据块(如 JPEG 的 MCUs)
    • JPEG 解码时会分段读取压缩内容,需要一个临时缓冲。
  • 从流中读取数据时作为 I/O buffer
    • 例如 InputStream → Skia 的内部解码器需要 buffer 装载部分数据。 接着调用了decodeFromWrappedStreams方法:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
private Bitmap decodeFromWrappedStreams(
    ImageReader imageReader,
    BitmapFactory.Options options,
    DownsampleStrategy downsampleStrategy,
    DecodeFormat decodeFormat,
    PreferredColorSpace preferredColorSpace,
    boolean isHardwareConfigAllowed,
    int requestedWidth,
    int requestedHeight,
    boolean fixBitmapToRequestedDimensions,
    DecodeCallbacks callbacks)
    throws IOException {
  long startTime = LogTime.getLogTime();
  int[] sourceDimensions = getDimensions(imageReader, options, callbacks, bitmapPool);
  int sourceWidth = sourceDimensions[0];
  int sourceHeight = sourceDimensions[1];
  String sourceMimeType = options.outMimeType;

  int orientation = imageReader.getImageOrientation();
  int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
  boolean isExifOrientationRequired = TransformationUtils.isExifOrientationRequired(orientation);

  int targetWidth =
      requestedWidth == Target.SIZE_ORIGINAL
          ? (isRotationRequired(degreesToRotate) ? sourceHeight : sourceWidth)
          : requestedWidth;
  int targetHeight =
      requestedHeight == Target.SIZE_ORIGINAL
          ? (isRotationRequired(degreesToRotate) ? sourceWidth : sourceHeight)
          : requestedHeight;

  ImageType imageType = imageReader.getImageType();

  calculateScaling(
      imageType,
      imageReader,
      callbacks,
      bitmapPool,
      downsampleStrategy,
      degreesToRotate,
      sourceWidth,
      sourceHeight,
      targetWidth,
      targetHeight,
      options);
  calculateConfig(
      imageReader,
      decodeFormat,
      isHardwareConfigAllowed,
      isExifOrientationRequired,
      options,
      targetWidth,
      targetHeight);

  boolean isKitKatOrGreater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
  if ((options.inSampleSize == 1 || isKitKatOrGreater) && shouldUsePool(imageType)) {
    int expectedWidth;
    int expectedHeight;
    if (sourceWidth >= 0
        && sourceHeight >= 0
        && fixBitmapToRequestedDimensions
        && isKitKatOrGreater) {
      expectedWidth = targetWidth;
      expectedHeight = targetHeight;
    } else {
      float densityMultiplier =
          isScaling(options) ? (float) options.inTargetDensity / options.inDensity : 1f;
      int sampleSize = options.inSampleSize;
      int downsampledWidth = (int) Math.ceil(sourceWidth / (float) sampleSize);
      int downsampledHeight = (int) Math.ceil(sourceHeight / (float) sampleSize);
      expectedWidth = Math.round(downsampledWidth * densityMultiplier);
      expectedHeight = Math.round(downsampledHeight * densityMultiplier);
    }
    if (expectedWidth > 0 && expectedHeight > 0) {
      setInBitmap(options, bitmapPool, expectedWidth, expectedHeight);
    }
  }

  if (preferredColorSpace != null) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
      boolean isP3Eligible =
          preferredColorSpace == PreferredColorSpace.DISPLAY_P3
              && options.outColorSpace != null
              && options.outColorSpace.isWideGamut();
      options.inPreferredColorSpace =
          ColorSpace.get(isP3Eligible ? ColorSpace.Named.DISPLAY_P3 : ColorSpace.Named.SRGB);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      options.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
    }
  }

  Bitmap downsampled = decodeStream(imageReader, options, callbacks, bitmapPool);
  callbacks.onDecodeComplete(bitmapPool, downsampled);

  Bitmap rotated = null;
  if (downsampled != null) {
    downsampled.setDensity(displayMetrics.densityDpi);

    rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
    if (!downsampled.equals(rotated)) {
      bitmapPool.put(downsampled);
    }
  }

  return rotated;
}

首先通过getDimensions方法获取图片的原始宽高:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private static int[] getDimensions(
    ImageReader imageReader,
    BitmapFactory.Options options,
    DecodeCallbacks decodeCallbacks,
    BitmapPool bitmapPool)
    throws IOException {
  options.inJustDecodeBounds = true;
  decodeStream(imageReader, options, decodeCallbacks, bitmapPool);
  options.inJustDecodeBounds = false;
  return new int[] {options.outWidth, options.outHeight};
}

在读取宽高前给Options的inJustDecodeBounds设置为true,获取完后,然后设置为false,这里是只读取图片的宽高,不进行解码加载到内存中。接着调用了decodeStream方法:

1
2
3
4
5
6
7
8
9
private static Bitmap decodeStream(
    ImageReader imageReader,
    BitmapFactory.Options options,
    DecodeCallbacks callbacks,
    BitmapPool bitmapPool)
    throws IOException {
  result = imageReader.decodeBitmap(options);
  return result;
}

此处调用了ByteBufferReader的decodeBitmap:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public Bitmap decodeBitmap(Options options) {
  return BitmapFactory.decodeStream(stream(), /* outPadding= */ null, options);
}

private InputStream stream() {
  return ByteBufferUtil.toStream(ByteBufferUtil.rewind(buffer));
}

public static InputStream toStream(@NonNull ByteBuffer buffer) {
  return new ByteBufferStream(buffer);
}

此处将ByteBuffer包装成ByteBufferStream的流,Glide自定义了一个bytebuffer的输入流。有兴趣的可以看看实现,这里就不展开了,获取到图片的原始宽高后,判断图片有没有进行旋转,如果有的话,则重新计算目标宽高,否则还是之前的目标宽高,接着调用calculateScaling方法来设置采样和缩放信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
private static void calculateScaling(
    ImageType imageType,
    ImageReader imageReader,
    DecodeCallbacks decodeCallbacks,
    BitmapPool bitmapPool,
    DownsampleStrategy downsampleStrategy,
    int degreesToRotate,
    int sourceWidth,
    int sourceHeight,
    int targetWidth,
    int targetHeight,
    BitmapFactory.Options options)
    throws IOException {
  int orientedSourceWidth = sourceWidth;
  int orientedSourceHeight = sourceHeight;

  final float exactScaleFactor =
      downsampleStrategy.getScaleFactor(
          orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight);

  SampleSizeRounding rounding =
      downsampleStrategy.getSampleSizeRounding(
          orientedSourceWidth, orientedSourceHeight, targetWidth, targetHeight);

  int outWidth = round(exactScaleFactor * orientedSourceWidth);
  int outHeight = round(exactScaleFactor * orientedSourceHeight);

  int widthScaleFactor = orientedSourceWidth / outWidth;
  int heightScaleFactor = orientedSourceHeight / outHeight;

  int scaleFactor =
      rounding == SampleSizeRounding.MEMORY
          ? Math.max(widthScaleFactor, heightScaleFactor)
          : Math.min(widthScaleFactor, heightScaleFactor);

  int powerOfTwoSampleSize;
  // BitmapFactory does not support downsampling wbmp files on platforms <= M. See b/27305903.
  if (Build.VERSION.SDK_INT <= 23
      && NO_DOWNSAMPLE_PRE_N_MIME_TYPES.contains(options.outMimeType)) {
    powerOfTwoSampleSize = 1;
  } else {
    powerOfTwoSampleSize = Math.max(1, Integer.highestOneBit(scaleFactor));
    if (rounding == SampleSizeRounding.MEMORY
        && powerOfTwoSampleSize < (1.f / exactScaleFactor)) {
      powerOfTwoSampleSize = powerOfTwoSampleSize << 1;
    }
  }

  options.inSampleSize = powerOfTwoSampleSize;
  int powerOfTwoWidth;
  int powerOfTwoHeight;
  if (imageType == ImageType.JPEG) {
    int nativeScaling = Math.min(powerOfTwoSampleSize, 8);
    powerOfTwoWidth = (int) Math.ceil(orientedSourceWidth / (float) nativeScaling);
    powerOfTwoHeight = (int) Math.ceil(orientedSourceHeight / (float) nativeScaling);
    int secondaryScaling = powerOfTwoSampleSize / 8;
    if (secondaryScaling > 0) {
      powerOfTwoWidth = powerOfTwoWidth / secondaryScaling;
      powerOfTwoHeight = powerOfTwoHeight / secondaryScaling;
    }
  } else if (imageType == ImageType.PNG || imageType == ImageType.PNG_A) {
    powerOfTwoWidth = (int) Math.floor(orientedSourceWidth / (float) powerOfTwoSampleSize);
    powerOfTwoHeight = (int) Math.floor(orientedSourceHeight / (float) powerOfTwoSampleSize);
  } else if (imageType.isWebp()) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
      powerOfTwoWidth = Math.round(orientedSourceWidth / (float) powerOfTwoSampleSize);
      powerOfTwoHeight = Math.round(orientedSourceHeight / (float) powerOfTwoSampleSize);
    } else {
      powerOfTwoWidth = (int) Math.floor(orientedSourceWidth / (float) powerOfTwoSampleSize);
      powerOfTwoHeight = (int) Math.floor(orientedSourceHeight / (float) powerOfTwoSampleSize);
    }
  } else if (orientedSourceWidth % powerOfTwoSampleSize != 0
      || orientedSourceHeight % powerOfTwoSampleSize != 0) {
    int[] dimensions = getDimensions(imageReader, options, decodeCallbacks, bitmapPool);
    powerOfTwoWidth = dimensions[0];
    powerOfTwoHeight = dimensions[1];
  } else {
    powerOfTwoWidth = orientedSourceWidth / powerOfTwoSampleSize;
    powerOfTwoHeight = orientedSourceHeight / powerOfTwoSampleSize;
  }

  double adjustedScaleFactor =
      downsampleStrategy.getScaleFactor(
          powerOfTwoWidth, powerOfTwoHeight, targetWidth, targetHeight);

  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    options.inTargetDensity = adjustTargetDensityForError(adjustedScaleFactor);
    options.inDensity = getDensityMultiplier(adjustedScaleFactor);
  }
  if (isScaling(options)) {
    options.inScaled = true;
  } else {
    options.inDensity = options.inTargetDensity = 0;
  }
}

首先通过downsampleStrategy.getScaleFactor获取缩放比,默认FitCenter:

1
2
3
4
5
6
7
8
private static class FitCenter extends DownsampleStrategy {
@Override
public float getScaleFactor(
    int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
  float widthPercentage = requestedWidth / (float) sourceWidth;
  float heightPercentage = requestedHeight / (float) sourceHeight;
  return Math.min(widthPercentage, heightPercentage);
}

需要宽高比上原始宽高的比取小的比例,比如在前面分析过一张544*184图片,imageView的宽高设置的wrap_content,获取到的requestedWidth和requestedHeight在小米10上是2206,所以getScaleFactor获取到的宽比例要小,差不多是4的样子。所以第一次算出来的exactScaleFactor是4,继续调用downsampleStrategy.getSampleSizeRounding是保质量还是保内存。在小米10上是保质量。然后算出outWidth和outWidth,它们是表示缩放后的大小,通过 sourceWidth*exactScaleFactor 算出来的,高也是如此。然后算出宽高各自的缩放比,只不过这里缩放比是取整,然后存储到widthScaleFactor和heightScaleFactor中,然后判断是保质量还是内存,如果是保内存,则取widthScaleFactor和heightScaleFactor中的大值,如果是保质量,则取它两的小值。将结果给到scaleFactor变量,最后计算采样率:

1
2
int powerOfTwoSampleSize = Math.max(1, Integer.highestOneBit(scaleFactor));
options.inSampleSize = powerOfTwoSampleSize;

Integer.highestOneBit是对某一个整数取2的n次幂,比如5的话,它的二进制是101,那么取最高位的1,其余位都是0,所以是4,最后将该数给到options的inSampleSize设置采样率。因为采样率取值是2的n次幂。那剩下的缩放该怎么办呢?交给了density来进行精度缩放,下面来看下下半部分的缩放:

  • JPEG
    • 先交给libjpeg-turbo进行原生缩放,缩放最大是8,并且使用了ceil向上取整,继续判断如果目标缩放大于8的话,则剩余的缩放交给skia进行缩放
  • PNG
    • png类就比较简单了,直接获取任意采样后的大小,然后交给了skia进行缩放,此时是向下取整
  • WebP
    • 和上面的png类似,也是获取任意采样后的大小,不过分版本是四舍五入还是向下取整
  • 原始宽/采样不能整除或者原始高/采样不能整除
    • 继续decode一次获取采样后的宽高,剩余交给skia进行缩放 接着再一次获取采样后的比例,获取到比例后,设置options的inTargetDensity和inDensity进行精确缩放,在底层做skia缩放的时候,会通过inTargetDensity/inDensity得到一个缩放比,但是这两个数都是一个整数,那么怎么保证两个整数相除得到的比例是最终的adjustedScaleFactor(float类型),下面看下Glide是如何算inTargetDensity和inDensity:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
options.inTargetDensity = adjustTargetDensityForError(adjustedScaleFactor);//计算最小误差的targetDensity
options.inDensity = getDensityMultiplier(adjustedScaleFactor);//inDensity使用一个很大的分母

private static int adjustTargetDensityForError(double adjustedScaleFactor) {
  int densityMultiplier = getDensityMultiplier(adjustedScaleFactor);
  int targetDensity = round(densityMultiplier * adjustedScaleFactor);
  float scaleFactorWithError = targetDensity / (float) densityMultiplier;
  double difference = adjustedScaleFactor / scaleFactorWithError;
  return round(difference * targetDensity);
}
private static int getDensityMultiplier(double adjustedScaleFactor) {
  return (int)
      Math.round(
          Integer.MAX_VALUE
              * (adjustedScaleFactor <= 1D ? adjustedScaleFactor : 1 / adjustedScaleFactor));
}

getDensityMultiplier方法是得到一个目标比例的分母,可以看出来它是一个很大的数,那自然而然,targetDensity(第一次的实际density)就是densityMultiplier(分母)*adjustedScaleFactor(目标比例)。然后得到实例比例(scaleFactorWithError),再看预期比例(adjustedScaleFactor)和实际比例(scaleFactorWithError)相隔多少倍,然后把这个差的倍数补上到第一次算的实际density(targetDensity)就得到最小误差的density,上面就是整个缩放的逻辑了。
再回到上面decode中的calculateConfig,它主要是通过判断是否有alpha通道来得到一个合适的解码方式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void calculateConfig(
    ImageReader imageReader,
    DecodeFormat format,
    boolean isHardwareConfigAllowed,
    boolean isExifOrientationRequired,
    BitmapFactory.Options optionsWithScaling,
    int targetWidth,
    int targetHeight) {

  if (format == DecodeFormat.PREFER_ARGB_8888
      || Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
    optionsWithScaling.inPreferredConfig = Bitmap.Config.ARGB_8888;
    return;
  }

  boolean hasAlpha = imageReader.getImageType().hasAlpha();

  optionsWithScaling.inPreferredConfig =
      hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
  if (optionsWithScaling.inPreferredConfig == Config.RGB_565) {
    optionsWithScaling.inDither = true;
  }
}

在Glide中format默认是 DecodeFormat.PREFER_ARGB_8888 ,如果要设置的话,通过注解可以配置,所以Glide优先是使用 Bitmap.Config.ARGB_8888 , 如果默认不是 DecodeFormat.PREFER_ARGB_8888 ,判断图片是否带透明信息,如果带的话,也是设置成 Bitmap.Config.ARGB_8888 ,否则是 Bitmap.Config.RGB_565 。scale和config处理完事后,最后就是options的inBitmap处理了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
float densityMultiplier =
    isScaling(options) ? (float) options.inTargetDensity / options.inDensity : 1f;
int sampleSize = options.inSampleSize;
int downsampledWidth = (int) Math.ceil(sourceWidth / (float) sampleSize);
int downsampledHeight = (int) Math.ceil(sourceHeight / (float) sampleSize);
expectedWidth = Math.round(downsampledWidth * densityMultiplier);
expectedHeight = Math.round(downsampledHeight * densityMultiplier);
if (expectedWidth > 0 && expectedHeight > 0) {
  setInBitmap(options, bitmapPool, expectedWidth, expectedHeight);
}

private static void setInBitmap(
    BitmapFactory.Options options, BitmapPool bitmapPool, int width, int height) {
  @Nullable Bitmap.Config expectedConfig = null;
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    if (options.inPreferredConfig == Config.HARDWARE) {
      return;
    }
    expectedConfig = options.outConfig;
  }

  if (expectedConfig == null) {
    expectedConfig = options.inPreferredConfig;
  }
  // BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
  options.inBitmap = bitmapPool.getDirty(width, height, expectedConfig);
}

计算最终的宽高,然后调用setInBitmap方法,在该方法里面通过bitmapPool中找到合适的bitmap,这样在解码的时候,可以用该bitmap的内存进行解码,不需要重复创建新内存,从而减少内存抖动。所有都完事后,最后就是再一次调用decodeStream将流转化成bitmap,然后回调给上层,整个解码完成。

编码过程

编码和解码是一个对立的关系,在主流程中讲过解码完成后,先后会回调到DecodeJob的onResourceDecoded和notifyEncodeAndRelease方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<Z> Resource<Z> onResourceDecoded(DataSource dataSource, @NonNull Resource<Z> decoded) {
  boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
  if (diskCacheStrategy.isResourceCacheable(
      isFromAlternateCacheKey, dataSource, encodeStrategy)) {
    final Key key;
    switch (encodeStrategy) {
      case SOURCE:
        key = new DataCacheKey(currentSourceKey, signature);
        break;
      case TRANSFORMED:
        key =
            new ResourceCacheKey(
                decodeHelper.getArrayPool(),
                currentSourceKey,
                signature,
                width,
                height,
                appliedTransformation,
                resourceSubClass,
                options);
        break;
    }

    LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
    deferredEncodeManager.init(key, encoder, lockedResult);
    result = lockedResult;
  }
}

private void notifyEncodeAndRelease(
    Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
  if (deferredEncodeManager.hasResourceToEncode()) {
    deferredEncodeManager.encode(diskCacheProvider, options);
  }
  onEncodeComplete();
}

在onResourceDecoded回调中首先通过DiskCacheStrategy的isResourceCacheable判断解码后是否可以编码,主要是看对应的DiskCacheStrategy是否可以编码,默认是DiskCacheStrategy. AUTOMATIC,它的isResourceCacheable方法如下:

1
2
3
4
5
6
7
@Override
public boolean isResourceCacheable(
    boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) {
  return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
          || dataSource == DataSource.LOCAL)
      && encodeStrategy == EncodeStrategy.TRANSFORMED;
}

isFromAlternateCacheKey表示是否能从LoadData中找到对应的sourceKey,如果从网络或从磁盘中找到原图的话,则currentSourceKey是一个GlideUrl的key,它是能从LoadData找得到的,所以isFromAlternateCacheKey为false,如果从网络返回的图片dataSource是REMOTE,如果是从磁盘中找到原图dataSource是DATA_DISK_CACHE,所以deferredEncodeManager的init方法不会调用,也就不会导致notifyEncodeAndRelease中调用deferredEncodeManager的encode,也就不会进行编码,如果我们将RequestOptions中的diskCacheStrategy换成DiskCacheStrategy. ALL就能实现编码,那我们看下DeferredEncodeManager的encode方法:

1
2
3
4
5
void encode(DiskCacheProvider diskCacheProvider, Options options) {
  diskCacheProvider
      .getDiskCache()
      .put(key, new DataCacheWriter<>(encoder, toEncode, options));
}

diskCacheProvider在上面解码的时候提过它是一个LazyDiskCacheProvider,在Engine中初始化的。在它里面是通过 DiskCache.Factory 的build方法创建DiskCache,在GlideBuilder的build方法中初始化的是InternalCacheDiskCacheFactory的 DiskCache.Factory ,最终创建的DiskLruCacheWrapper,从名字看它是一个包装类,实际干活的是DiskLruCache,它是glide内部实现的LRU算法的磁盘缓存类。这个跟上面的解码之前将InputStream保存到本地磁盘,也就是原图保存是一样的。最终会走到DataCacheWriter的write方法:

1
2
3
4
5
6
class DataCacheWriter<DataType> implements DiskCache.Writer {
  @Override
  public boolean write(@NonNull File file) {
    return encoder.encode(data, file, options);
  }
}

encoder是在上面onResourceDecoded方法中从Registry的resourceEncoderRegistry中根据Resource的getResourceClass返回的Class取到的,如果解码返回一个Bitmap的话,那么此时得到的是一个BitmapEncoder:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static final Option<Integer> COMPRESSION_QUALITY =
    Option.memory("com.bumptech.glide.load.resource.bitmap.BitmapEncoder.CompressionQuality", 90);
public boolean encode(
    @NonNull Resource<Bitmap> resource, @NonNull File file, @NonNull Options options) {
  final Bitmap bitmap = resource.get();
  Bitmap.CompressFormat format = getFormat(bitmap, options);
  int quality = options.get(COMPRESSION_QUALITY);
  boolean success = false;
  OutputStream os = null;
  OutputStream os = new FileOutputStream(file);
  if (arrayPool != null) {
    os = new BufferedOutputStream(os, arrayPool);
  }
  bitmap.compress(format, quality, os);
  success = true;
  return success;
}

private Bitmap.CompressFormat getFormat(Bitmap bitmap, Options options) {
  Bitmap.CompressFormat format = options.get(COMPRESSION_FORMAT);
  if (format != null) {
    return format;
  } else if (bitmap.hasAlpha()) {
    return Bitmap.CompressFormat.PNG;
  } else {
    return Bitmap.CompressFormat.JPEG;
  }
}

默认的编码质量是90,并且默认的编码格式是如果图片带有透明通道则采用PNG无损压缩,否则使用JPEG有损压缩。如果字节池子存在,则构建一个BufferedOutputStream的输出流,它使用的byte数组来自于字节池子中的,默认字节大小是64KB的缓冲区,每次byte数组达到了64KB的时候,进行同步到底层的FileOutStream中。因为底层的FileOutputStream每次write都会触发一次系统调用,系统调用成本高,多次小块写入会产生:写入时间变长、CPU降速、磁盘碎片、整体编码耗时增加。同时BufferedOutputStream使用的是字节池子中的字节数组,降低GC的触发。

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy