Glide源码阅读之单例模式
  DuRoeuPpxCCW 2023年11月02日 68 0


为便于阅读建议读者先看看前一篇Glide组合使用说明
《Glide使用方式记录–没有全部亲测,大家可以根据实际需要选用》

后期所有Glide解析都是围绕使用实用方面展开。践行“拿来即用”主义

单例定义

属于创建型模式;全局只创建一个对象。

Glide使用形式

Glide.with(context)。。。

Glide单例写法

context检查

/**
   * @see #with(android.app.Activity)
   * @see #with(android.app.Fragment)
   * @see #with(androidx.fragment.app.Fragment)
   * @see #with(androidx.fragment.app.FragmentActivity)
   */
  @NonNull
  public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }
  。。。。。。

@NonNull
  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }

Glide.get(context)单例开始

@GuardedBy("Glide.class")
  private static volatile Glide glide;

/**
   * Get the singleton.
   *
   * @return the singleton
   */
  @NonNull
  // Double checked locking is safe here.
  @SuppressWarnings("GuardedBy")
  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;

volatile:在只有DCL没有volatile的懒加载单例模式中,仍然存在着并发陷阱。感兴趣的可以看看参考《单例模式volatile》或者搜索“volatile 单例模式”相信里面的并发和内存模型的原理会让你大呼精彩

@Nullable
  @SuppressWarnings({"unchecked", "TryWithIdenticalCatches", "PMD.UnusedFormalParameter"})
  private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules(Context context) {
    GeneratedAppGlideModule result = null;
    try {
      Class<GeneratedAppGlideModule> clazz =
          (Class<GeneratedAppGlideModule>)
              Class.forName("com.bumptech.glide.GeneratedAppGlideModuleImpl");
      result =
          clazz.getDeclaredConstructor(Context.class).newInstance(context.getApplicationContext());
    } catch (ClassNotFoundException e) {
      if (Log.isLoggable(TAG, Log.WARN)) {
        Log.w(
            TAG,
            "Failed to find GeneratedAppGlideModule. You should include an"
                + " annotationProcessor compile dependency on com.github.bumptech.glide:compiler"
                + " in your application and a @GlideModule annotated AppGlideModule implementation"
                + " or LibraryGlideModules will be silently ignored");
      }
      // These exceptions can't be squashed across all versions of Android.
    } catch (InstantiationException e) {
      throwIncorrectGlideModule(e);
    } catch (IllegalAccessException e) {
      throwIncorrectGlideModule(e);
    } catch (NoSuchMethodException e) {
      throwIncorrectGlideModule(e);
    } catch (InvocationTargetException e) {
      throwIncorrectGlideModule(e);
    }
    return result;
  }

初次看到的GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
有些诧异,在之前的旧版本里是没有的,看到源码的日志:

"Failed to find GeneratedAppGlideModule. You should include an"
                + " annotationProcessor compile dependency on com.github.bumptech.glide:compiler"
                + " in your application and a @GlideModule annotated AppGlideModule implementation"
                + " or LibraryGlideModules will be silently ignored"

很明显是来验证是否添加依赖和注解
依赖例子如下:

annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'

注解例子如下:

@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}

所以到这可以看出这是进行使用环境搭建检查。为后续单例对象建立护航。

单例对象建立

@GuardedBy("Glide.class")
  private static void checkAndInitializeGlide(
      @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
    // In the thread running initGlide(), one or more classes may call Glide.get(context).
    // Without this check, those calls could trigger infinite recursion.
    if (isInitializing) {
      throw new IllegalStateException(
          "You cannot call Glide.get() in registerComponents(),"
              + " use the provided Glide instance instead");
    }
    isInitializing = true;
    initializeGlide(context, generatedAppGlideModule);
    isInitializing = false;
  }

初始化标识检查,接着往下:

@GuardedBy("Glide.class")
  private static void initializeGlide(
      @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
    initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
  }
@GuardedBy("Glide.class")
  @SuppressWarnings("deprecation")
  private static void initializeGlide(
      @NonNull Context context,
      @NonNull GlideBuilder builder,
      @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
    Context applicationContext = context.getApplicationContext();
    //创建空集合对象。避开NullPointObject【建议列表初始化空对象列表】详情参考“附录1”
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();

//
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      manifestModules = new ManifestParser(applicationContext).parse();
    }
//Glide Generated API 可在 Application 和 Library 中被扩展。扩展使用被注解的静态方法来添加新的选项、修改现有选项、甚至添加额外的类型支持
    if (annotationGeneratedModule != null
        && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
      Set<Class<?>> excludedModuleClasses = annotationGeneratedModule.getExcludedModuleClasses();
      Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
      while (iterator.hasNext()) {
        com.bumptech.glide.module.GlideModule current = iterator.next();
        if (!excludedModuleClasses.contains(current.getClass())) {
          continue;
        }
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
        }
        iterator.remove();
      }
    }

    if (Log.isLoggable(TAG, Log.DEBUG)) {
      for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
        Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
      }
    }
//被 @GlideType 注解的静态方法用于扩展 RequestManager 。被 @GlideType 注解的方法允许你添加对新的资源类型的支持,包括指定默认选项。
    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory()
            : null;
    builder.setRequestManagerFactory(factory);
    //用 @GlideOption 注解的静态方法用于扩展 RequestOptions 。GlideOption 可以:

定义一个在 Application 模块中频繁使用的选项集合。
创建新的选项,通常与 Glide 的 Option 类一起使用。
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
//构建对象
    Glide glide = builder.build(applicationContext);
    //注册自定义 manifestModules
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      try {
        module.registerComponents(applicationContext, glide, glide.registry);
      } catch (AbstractMethodError e) {
        throw new IllegalStateException(
            "Attempting to register a Glide v3 module. If you see this, you or one of your"
                + " dependencies may be including Glide v3 even though you're using Glide v4."
                + " You'll need to find and remove (or update) the offending dependency."
                + " The v3 module name is: "
                + module.getClass().getName(),
            e);
      }
    }
    //注册默认的 Module
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    //注册回调
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }

嗯!这个单例对象的建立比较复杂了,从上到下大致作用如下:

  1. 集成库可以为 Generated API 扩展自定义选项。
  2. 在 Application 模块中可将常用的选项组打包成一个选项在Generated API 中使用
  3. GlideOption - 为 RequestOptions 添加一个自定义的选项。
  4. GlideType - 添加对新的资源类型的支持(GIF,SVG 等等)。

annotationGeneratedModule生效判断以及应用设置

if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      manifestModules = new ManifestParser(applicationContext).parse();
    }

清单解析

为了简化迁移过程,尽管清单解析和旧的 GlideModule 接口已被废弃,但它们在 v4 版本中仍被支持。AppGlideModule,LibraryGlideModule,与已废弃的 GlideModule 可以在一个应用中共存。

然而,为了避免检查元数据的性能天花板(以及相关的 bugs ),你可以在迁移完成后禁用掉清单解析,在你的 AppGlideModule 中复写一个方法:

@GlideModule
public class GiphyGlideModule extends AppGlideModule {
  @Override
  public boolean isManifestParsingEnabled() {
    return false;
  }

  ...
}

清单解析不是本次单例模式解析重点。因此暂不展开说明ManifestParser

Glide具体参数构建过程如下

com.bumptech.glide.GlideBuilder

private final Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions = new ArrayMap<>();
  private final GlideExperiments.Builder glideExperimentsBuilder = new GlideExperiments.Builder();
  private Engine engine;
  private BitmapPool bitmapPool;
  private ArrayPool arrayPool;
  private MemoryCache memoryCache;
  private GlideExecutor sourceExecutor;
  private GlideExecutor diskCacheExecutor;
  private DiskCache.Factory diskCacheFactory;
  private MemorySizeCalculator memorySizeCalculator;
  private ConnectivityMonitorFactory connectivityMonitorFactory;
  private int logLevel = Log.INFO;
  private RequestOptionsFactory defaultRequestOptionsFactory =
      new RequestOptionsFactory() {
        @NonNull
        @Override
        public RequestOptions build() {
          return new RequestOptions();
        }
      };
  @Nullable private RequestManagerFactory requestManagerFactory;
  private GlideExecutor animationExecutor;
  private boolean isActiveResourceRetentionAllowed;
  @Nullable private List<RequestListener<Object>> defaultRequestListeners;

  ......
  
@NonNull
  Glide build(@NonNull Context context) {
    if (sourceExecutor == null) {
      sourceExecutor = GlideExecutor.newSourceExecutor();
    }

    if (diskCacheExecutor == null) {
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }

    if (animationExecutor == null) {
      animationExecutor = GlideExecutor.newAnimationExecutor();
    }

    if (memorySizeCalculator == null) {
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }

    if (connectivityMonitorFactory == null) {
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }

    if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }

    if (arrayPool == null) {
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

    if (engine == null) {
      engine =
          new Engine(
              memoryCache,
              diskCacheFactory,
              diskCacheExecutor,
              sourceExecutor,
              GlideExecutor.newUnlimitedSourceExecutor(),
              animationExecutor,
              isActiveResourceRetentionAllowed);
    }

    if (defaultRequestListeners == null) {
      defaultRequestListeners = Collections.emptyList();
    } else {
      defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
    }

    GlideExperiments experiments = glideExperimentsBuilder.build();
    RequestManagerRetriever requestManagerRetriever =
        new RequestManagerRetriever(requestManagerFactory, experiments);

    return new Glide(
        context,
        engine,
        memoryCache,
        bitmapPool,
        arrayPool,
        requestManagerRetriever,
        connectivityMonitorFactory,
        logLevel,
        defaultRequestOptionsFactory,
        defaultTransitionOptions,
        defaultRequestListeners,
        experiments);
  }

构建参数比较多,不过大部分都是见名知意。理解起来也不太难。
到这里整个单例模式使用过程解析完毕!!!

总结

单例方面

Glide单例的使用过程有很多的借鉴的地方。作者本人能力有限如有表达不明确和不清楚请留言讨论,欢迎大家斧正!
设计思路大致如下:
单例构建环境检查:
使用了多种方式来检查环境

  1. context检查 Preconditions.checkNotNull.checkNotNull(@Nullable T arg, @NonNull
    String message)
  2. 单例模式第一次检查if (glide == null)
  3. 检查annotationGeneratedModule自定义
  4. 单例第二次检查synchronized (Glide.class) {
  5. 标识符检查isInitializing
  6. excludedModuleClasses扩展项加载
  7. RequestManagerFactory加载等

很多时候对使用环境的检查越深刻和仔细,才能保证功能的正常使用。Glide的对环境的触探对于SDK开发者来说是很值得仔仔细细揣摩和学习实践的。

对于经常使用单例模式的场景,本人建议也借鉴Glide的这种经过多年的Android版本更新后向上向下得兼容性得考虑。在源码里可以很清楚地看到单例模式的使用结构也跃迁了多次。

通过本次解析可以体会到Glide的对外包容性,可以进行比较深度的自定义。相信开发的童鞋都用过或者自己设计过SDK集成包。Glide这样的简洁的使用方式很值得学习。对功能模块设计和开放也能促进解耦。

空列表Collections.emptyList()

这个对于Android开发者来说很有实用意义。在listview、RecyclerView等列表里经常用到。如果在初始化时使用Collections.emptyList()。即可避免了空指向异常类似
size(),isEmpty(),contains(),containsAll()等方法就可以不用进行繁琐的空判断了。

空对象扩展(空对象模式,也算设计模式的一种)

如果对一个类或对象的空判断操作比较频繁,个人建议空判断操作大于3次即可考虑扩展出空对象。当然也结合实际情况。比如对类数量有要求的(项目上在集成的过程中有些需求总是很措手不及没有道理可讲的)。

如果后期再丰富空对象模式使用场景作者会重新写一篇,先在这挖个坑。

附录1:

Collections.emptyList()
java.util.Collections

具体过程如下:

public static final Set EMPTY_SET = new Collections.EmptySet();
    public static final List EMPTY_LIST = new Collections.EmptyList();
    public static final Map EMPTY_MAP = new Collections.EmptyMap();


public static final <T> List<T> emptyList() {
        return EMPTY_LIST;
    }

private static class EmptyList<E> extends AbstractList<E> implements RandomAccess, Serializable {
        private static final long serialVersionUID = 8842843931221139166L;

        private EmptyList() {
        }

        public Iterator<E> iterator() {
            return Collections.emptyIterator();
        }

        public ListIterator<E> listIterator() {
            return Collections.emptyListIterator();
        }

        public int size() {
            return 0;
        }

        public boolean isEmpty() {
            return true;
        }

        public boolean contains(Object var1) {
            return false;
        }

        public boolean containsAll(Collection<?> var1) {
            return var1.isEmpty();
        }

        public Object[] toArray() {
            return new Object[0];
        }

        public <T> T[] toArray(T[] var1) {
            if (var1.length > 0) {
                var1[0] = null;
            }

            return var1;
        }

        public E get(int var1) {
            throw new IndexOutOfBoundsException("Index: " + var1);
        }

        public boolean equals(Object var1) {
            return var1 instanceof List && ((List)var1).isEmpty();
        }

        public int hashCode() {
            return 1;
        }

        public boolean removeIf(Predicate<? super E> var1) {
            Objects.requireNonNull(var1);
            return false;
        }

        public void replaceAll(UnaryOperator<E> var1) {
            Objects.requireNonNull(var1);
        }

        public void sort(Comparator<? super E> var1) {
        }

        public void forEach(Consumer<? super E> var1) {
            Objects.requireNonNull(var1);
        }

        public Spliterator<E> spliterator() {
            return Spliterators.emptySpliterator();
        }

        private Object readResolve() {
            return Collections.EMPTY_LIST;
        }
    }

好用的api接口开发工具

历时一年半多开发终于smartApi-v1.0.0版本在2023-09-15晚十点正式上线
smartApi是一款对标国外的postman的api调试开发工具,由于开发人力就作者一个所以人力有限,因此v1.0.0版本功能进行精简,大功能项有:

  • api参数填写
  • api请求响应数据展示
  • PDF形式的分享文档
  • Mock本地化解决方案
  • api列表数据本地化处理
  • 再加上UI方面的打磨

下面是一段smartApi使用介绍:

Glide源码阅读之单例模式_设计模式

下载地址:

https://pan.baidu.com/s/1NFm7QiUYRW_nu-ECwnKZ1g?pwd=51ct

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  xaeiTka4h8LY   2024年05月17日   52   0   0 数据库JavaSQL
  2iBE5Ikkruz5   2023年12月12日   92   0   0 JavaJavaredisredis
DuRoeuPpxCCW