Glide中优秀代码总结

Glide单例初始化

 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
private static volatile Glide glide;
public static Glide get(@NonNull Context context) {
  if (glide == null) {
    GeneratedAppGlideModule annotationGeneratedModule =
        getAnnotationGeneratedGlideModules(context.getApplicationContext());
    synchronized (Glide.class) {
      if (glide == null) {
        checkAndInitializeGlide(context, annotationGeneratedModule);
      }
    }
  }
  return glide;
}

private static volatile boolean isInitializing;
static void checkAndInitializeGlide(
    @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
  if (isInitializing) {
    throw new IllegalStateException(
        "Glide has been called recursively, this is probably an internal library error!");
  }
  isInitializing = true;
  try {
    initializeGlide(context, generatedAppGlideModule);
  } finally {
    isInitializing = false;
  }
}

上面Glide对象初始化使用DCL双重锁判断,并且checkAndInitializeGlide方法中使用了isInitializing变量表示是否正在初始化。这个isInitializing变量解决单线程递归调用Glide.get方法会创建多个Glide实例。因为在DCL模式中,单线程是能重入synchronized锁的。为了避免单线程能多次创建Glide实例,所以加了标志表示是否正在加载。

Builder建造者模式

建造者模式是属于创建型中的一种,用于创建复杂对象,将对象的构建与表示分离,以便相同的构建过程可以创建不同的表示。

  • GlideBuilder
    • 用于创建Glide对象,因为Glide对象比较复杂,所以将Glide的创建交给了GlideBuilder,同样的构建过程,可以表示不同的Glide对象。比如指定sourceExecutor(加载网络资源的线程池)、diskCacheExecutor(加载本地磁盘缓存的线程池)等。
  • RequestBuilder
    • 它是继承自BaseRequestOptions,因此在构建request过程中,它是充当了builder创建模式来创建request,比如配置override大小、placeholder等。

原型模式

RequestBuilder的clone

在RequestBuilder中的into方法里面每次先clone一份RequestOptions出来:

 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
class RequestBuilder{
    public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {

        BaseRequestOptions<?> requestOptions = this;
        if (!requestOptions.isTransformationSet()
            && requestOptions.isTransformationAllowed()
            && view.getScaleType() != null) {
            switch (view.getScaleType()) {
            case CENTER_CROP:
                requestOptions = requestOptions.clone().optionalCenterCrop();
                break;
            case CENTER_INSIDE:
                requestOptions = requestOptions.clone().optionalCenterInside();
                break;
            case FIT_CENTER:
            case FIT_START:
            case FIT_END:
                requestOptions = requestOptions.clone().optionalFitCenter();
                break;
            case FIT_XY:
                requestOptions = requestOptions.clone().optionalCenterInside();
                break;
            case CENTER:
            case MATRIX:
            default:
            }
        }

        return into(
            glideContext.buildImageViewTarget(view, transcodeClass),
            /* targetListener= */ null,
            requestOptions,
            Executors.mainThreadExecutor());
    }
    
    public T clone() {
        try {
            BaseRequestOptions<?> result = (BaseRequestOptions<?>) super.clone();
            result.options = new Options();
            result.options.putAll(options);
            result.transformations = new CachedHashCodeArrayMap<>();
            result.transformations.putAll(transformations);
            result.isLocked = false;
            result.isAutoCloneEnabled = false;
            return (T) result;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

requestOptions通过clone方法构造一个新的BaseRequestOptions,原型模式的作用是通过已有的对象属性构建一个新的对象,好处是创建新对象时复用已有配置,避免重复设置。新对象与原对象相互独立,互不影响。此处好处有如下几点:

  • 支持 RequestBuilder 复用
    • RequestBuilder是可以复用的,因为load方法会返回requestBuilder,而into可以作用到不同的imageView上,如果先作用到imageView1上,它的scaleType是fitCenter,而后又作用到imageView2上,此时imageView是centerCrop模式展示。如果没有clone的话,imageView2加载完后会影响到imageView1的展示。因为scaleType的不同会给requestOptions设置strategy和transformation不同。
  • 防止后续修改影响已发起的请求
    • 还是拿两个图片共用一个RequestBuilder来说,比如imageView1需要通过requestOptions设置override(100,100)的大小,此时imageView1还在请求,而此时imageView2通过requestOptions设置override(200,200),那么此时如果没有clone出一个新的requestOptions会导致imageView1加载的大小不对应。
  • 多线程安全问题
    • into方法如果在不同的线程被调用,如果没有clone出一个新的requestOptions,那么会存在线程安全问题。

工厂模式

DiskCache的创建

glide中使用的工厂模式分为工厂方法模式和抽象工厂模式,首先看下哪些地方用到了工厂方法模式:

  • DiskCache的创建
    • 在GlideBuilder中默认初始化了DiskCache.Factory,他其实是一个InternalCacheDiskCacheFactory:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public interface DiskCache {
  interface Factory {
    @Nullable
    DiskCache build();
  }
}  
public final class GlideBuilder {
  private DiskCache.Factory diskCacheFactory;
  Glide build(){
    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
  }
}

接着将diskCacheFactory给到Engine,在Engine中会将diskCacheFactory给到LazyDiskCacheProvider,它是实现了DecodeJob.DiskCacheProvider接口,可以看出来它是在DecodeJob中需要使用到该DiskCache.Factory,我们设计接口的时候,需要将接口保持最小范围,如果只在外部类中使用该接口,将接口定义在外部类中,体现了设计原则中的封装原则,从而减少对外暴露。第二点体现了高内聚原则,将相关的接口和类放在一起,提高内聚性。

1
2
3
4
5
class DecodeJob{
  interface DiskCacheProvider {
    DiskCache getDiskCache();
  }
}

并且LazyDiskCacheProvider的定义是在Engine中定义,这也体现了高内聚和封装原则,不仅是接口,类与类之间也能体现出来:

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

LazyDiskCacheProvider从字面意思也能看出来,它是一个延迟获取DiskCache的封装类。如果没有它,那就得在Glide初始化的时候创建DiskCache了。因为DiskCache是一个重量级的类,里面涉及到文件目录的创建等。并且在LazyDiskCacheProvider中保证了线程安全来创建DiskCache。

RequestOptions的创建

  • 它也是通过工厂方法模式来创建的:
1
2
3
4
5
6
class Glide{
  public interface RequestOptionsFactory {
    @NonNull
    RequestOptions build();
  }
}

它是在Glide外部类中定义的接口,也是和上面类似,再来看下它的接口实现类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class GlideBuilder{
  private RequestOptionsFactory defaultRequestOptionsFactory =
      new RequestOptionsFactory() {
        @NonNull
        @Override
        public RequestOptions build() {
          return new RequestOptions();
        }
      };
}

在Glide创建的时候,直接把defaultRequestOptionsFactory传给了Glide对象,也没有判断外界有没有传入RequestOptionsFactory,可以看出来它无需外界进行扩展,这点和DiskCache.Factory不太一样。由于RequestOptions是一个轻量级的,所以它也无需延迟初始化,它是在RequestManager初始化中调用GlideContext的getDefaultRequestOptions方法来创建的:

1
2
3
4
5
6
7
8
class GlideContext{
  public synchronized RequestOptions getDefaultRequestOptions() {
    if (defaultRequestOptions == null) {
      defaultRequestOptions = defaultRequestOptionsFactory.build().lock();
    }
    return defaultRequestOptions;
  }
}

Registry的创建

  • Registry是通过GlideSupplier接口创建的,也是一个工厂方法模式:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
      }
    };
  }
}

也是一个类中内部接口,不过该接口是一个泛型。外界需要调用memorize方法,并传入一个GlideSupplier的实现类,在前面介绍Registry创建过程中,它是在GlideContext的getRegistry方法进行获取的,而默认是调用memorize方法获取到GlideSupplier对象,等到需要Registry时候才会调用GlideSupplier的get方法,显而易见这是一个延迟初始化,并且这里使用了静态代理,将Registry的最终获取交给了外界传入的Registry。同时匿名内部类的GlideSupplier通过双重锁保证了线程安全,这里其实和上面的DiskCache的创建有点异曲同工之妙。

ModelLoader的创建

  • ModelLoader是通过ModelLoaderFactory接口来创建的:
1
2
3
4
public interface ModelLoaderFactory<T, Y> {
  @NonNull
  ModelLoader<T, Y> build(@NonNull MultiModelLoaderFactory multiFactory);
}

在RegistryFactory中先把modelClass、dataClass、ModelLoaderFactory注册到MultiModelLoaderFactory中,组转成一个个的Entry对象,等到要使用ModelLoader时候,通过modelClass和dataClass过滤出ModelLoaderFactory,然后通过ModelLoaderFactory创建出ModelLoader,接着获取到LoadData,最后获取到DataFetcher。 在MultiModelLoader中装载了ModelLoader的集合,包含了多个子节点,在获取LoadData的时候使用了责任链模式,依次让每个ModelLoader尝试进行获取LoadData,同时这里也体现出了迭代器模式。

DataRewinder的创建

  • DataRewinder是一个数据重置器,它也是通过方法工厂来创建的
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public interface DataRewinder<T> {

  interface Factory<T> {
    @NonNull
    DataRewinder<T> build(@NonNull T data);

    @NonNull
    Class<T> getDataClass();
  }

  @NonNull
  T rewindAndGet() throws IOException;
}

也是在外部接口中定义内部接口。在前面介绍InputStream转化成File的时候讲过,先把各种DataRewinder.Factory注册到DataRewinderRegistry中,然后通过传入的class类型获取到对应的DataRewinder.Factory,接着通过build方法获取到DataRewinder。

Encoder的获取

Encoder是编码器的接口类,Encoder的创建虽然没有用到Factory,但是它是和上面DataRewinder的创建形成了对比,Encoder它是直接在Registry阶段直接包转成Entry,然后在获取的时候直接从Entry中获取到Encoder。 Encoder为什么它不需要Factory创建呢?而DataRewinder需要通过Factory来创建?

特性 Encoder DataRewinder
状态 无状态 有状态
数据依赖 数据通过方法参数传入 数据在构造时注入
实例复用 ✅ 可复用,一个实例处理多个请求 ❌ 每次需要新实例
生命周期 应用级别,长期存活 请求级别,用完释放
资源管理 不需要 cleanup 需要 cleanup() 释放资源

比如在前面讲的将原始图片保存到本地的时候说过StreamEncoder:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class StreamEncoder implements Encoder<InputStream>{
  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;
  }
}

它不持有数据,所以是无状态的。数据是通过方法参数传入。并且StreamEncoder是应用级别的,它无需每次进行创建。所以它无需通过factory进行延迟初始化。而在DataRewinder中它是需要持有数据的,比如前面讲的InputStreamRewinder:

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

它需要持有数据,因为它需要多次对流进行重置位置。并且每次获取图片都需要创建一个新的DataRewinder,所以它需要通过factory来创建不同的DataRewinder,因为它是跟数据绑定在一块的,因此用到了延迟初始化。

解码(静态代理)

  • 首先了解几个概念,dataClass:数据源类型,比如从网络获取的数据类型是byteBuffer.class类型。resourceClass:解码后的资源类型,比如bitmap的解码,它是BitmapDrawable.class,也就是bitmap的drawable包装。transcodeClass:最终转换类型,比如要展示到imageView上的时候,此时是Drawable.class类型。
  • 在注册的时候,会按照bucket、dataClass、resourceClass、decoder四个参数组转成Entry,并且该Entry是定义在ResourceDecoderRegistry的私有内部类中。保证高内聚,低耦合。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
private static class Entry<T, R> {
  private final Class<T> dataClass;
  @Synthetic final Class<R> resourceClass;
  @Synthetic final ResourceDecoder<T, R> decoder;
  public Entry(
      @NonNull Class<T> dataClass,
      @NonNull Class<R> resourceClass,
      ResourceDecoder<T, R> decoder) {
    this.dataClass = dataClass;
    this.resourceClass = resourceClass;
    this.decoder = decoder;
  }
  public boolean handles(@NonNull Class<?> dataClass, @NonNull Class<?> resourceClass) {
    return this.dataClass.isAssignableFrom(dataClass)
        && resourceClass.isAssignableFrom(this.resourceClass);
  }
}

最终将这些Entry装载到ResourceDecoderRegistry的decoders这个map中。比如在前面介绍的BitmapDrawableDecoder,它就是按照bucket= “BitmapDrawable”,dataClass=“ByteBuffer.class”,resourceClass=“BitmapDrawable.class”,decoder=BitmapDrawableDecoder组成的entry来获取的。并且在BitmapDrawableDecoder中使用了代理模式,实际代理到ByteBufferBitmapDecoder,最终会调用到Downsampler中。

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计