Glide源码阅读之单例模式
为便于阅读建议读者先看看前一篇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;
}
嗯!这个单例对象的建立比较复杂了,从上到下大致作用如下:
- 集成库可以为 Generated API 扩展自定义选项。
- 在 Application 模块中可将常用的选项组打包成一个选项在Generated API 中使用
- GlideOption - 为 RequestOptions 添加一个自定义的选项。
- 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单例的使用过程有很多的借鉴的地方。作者本人能力有限如有表达不明确和不清楚请留言讨论,欢迎大家斧正!
设计思路大致如下:
单例构建环境检查:
使用了多种方式来检查环境
- context检查 Preconditions.checkNotNull.checkNotNull(@Nullable T arg, @NonNull
String message) - 单例模式第一次检查if (glide == null)
- 检查annotationGeneratedModule自定义
- 单例第二次检查synchronized (Glide.class) {
- 标识符检查isInitializing
- excludedModuleClasses扩展项加载
- 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;
}
}
更多设计模式解读
单例模式
Glide设计模式之单例模式
空对象模式
Glide设计模式之空对象模式【EmptyModelLoader】【EmptyList<E>
建造者模式
Glide多种组合使用方式记录–没有全部亲测,大家可以根据实际需要选用
Glide设计模式之建造者(builder)模式1【GlideBuilder】
Glide设计模式之建造者(builder)模式2【RequestBuilder】
Glide设计模式之建造者(builder)模式3【RequestOptions】【BaseRequestOptions】
Glide设计模式之建造者(builder)模式4总结【MemorySizeCalculator】【GlideExecutor】【PreFillType】【LazyHeaders】
工厂模式
Glide设计模式之工厂模式1【ModelLoaderFactory】
Glide设计模式之工厂模式2【DiskCache.Factory】
Glide工厂模式3【TransitionFactory】【Transition】
Glide设计模式之工厂模式4总结
自研产品推荐
历时一年半多开发终于smartApi-v1.0.0版本在2023-09-15晚十点正式上线
smartApi是一款对标国外的postman的api调试开发工具,由于开发人力就作者一个所以人力有限,因此v1.0.0版本功能进行精简,大功能项有:
- api参数填写
- api请求响应数据展示
- PDF形式的分享文档
- Mock本地化解决方案
- api列表数据本地化处理
- 再加上UI方面的打磨
为了更好服务大家把之前的公众号和软件激活结合,如有疑问请大家反馈到公众号即可,下个版本30%以上的更新会来自公众号的反馈。
嗯!先解释不上服务端原因,API调试工具的绝大多数时候就是一个数据模型、数据处理、数据模型理解共识的问题解决工具,所以作者结合自己十多年开发使用的一些痛点来打造的,再加上服务端开发一般是面向企业的,作者目前没有精力和时间去打造企业服务。再加上没有资金投入所以服务端开发会滞后,至于什么时候会进行开发,这个要看募资情况和用户反馈综合考虑。虽然目前国内有些比较知名的api工具了,但作者使用后还是觉得和实际使用场景不符。如果有相关吐槽也可以在作者的公众号里反馈蛤!
下面是一段smartApi使用介绍: