前言
最近排查了一个项目中的因Glide
在加载图片引起的崩溃,大概的异常时这样的:
com.xxx.xxx.GlideRequests can not cast to com.xxx.xxx.GlideRequests
先说一下背景:这是由于主模块和Library模块中同时依赖了Glide 4.x
,并同时使用了4.x中的Generated API,而且两个模块下都使用了AppGlideModule
。
且同时使用了GlideApp
进行图片加载。
在Glide
文档中有这样一段话:
程序库一定 不要 包含
AppGlideModule
实现。这么做将会阻止依赖该库的任何应用程序管理它们的依赖,或配置诸如 Glide 缓存大小和位置之类的选项。此外,如果两个程序库都包含AppGlideModule
,应用程序将无法在同时依赖两个库的情况下通过编译,而不得不在二者之中做出取舍。
也就是说其实笔者遇到的问题是一个错误的用法。那么秉着查根问底的精神,笔者又研究了一下出现该崩溃的具体原因是什么?所以就有了这篇文章。
模拟代码及注解生成代码
首先我们需要准备两个模块,并分别实现AppGlideModule
:
- 主模块
me.xcyoung.study
package me.xcyoung.study;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
public class MyAppModule extends AppGlideModule {
}
复制代码
- lib模块
me.xcyoung.study.lib.test
package me.xcyoung.lib.test;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
public class MyLibModule extends AppGlideModule {
}
复制代码
编译后会在各自模块中生成:
这里注解生成的代码,可以先有个印象,后面有涉及到。
Glide初始化
简单介绍一下Glide的初始化。Glide会全局维护一个静态的Glide对象,作为App加载图片的唯一环境。ps:属于Application级别的事情,不区分是哪个module的代码。
private static volatile Glide glide;
复制代码
在使用的时候,先通过get方法获取该静态变量,如果没有就尝试初始化。ps:这里是一个懒加载的设计
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
// 1
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
复制代码
接下来我们把关注点放在get方法中的调用的getAnnotationGeneratedGlideModules
方法。
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) {
...
复制代码
方法通过反射,实例了一个com.bumptech.glide.GeneratedAppGlideModuleImpl
对象。这个也就是在模拟代码及注解生成代码
一节中说到的注解生成的代码。紧接着该类就会在初始化的过程中触发我们自定义的全局配置,这里就不详细解析了。
根据上面的截图可以发现,由于两个模块都实现了AppGlideModule
,所以两个模块中都生成了GeneratedAppGlideModuleImpl
。所以接下来我们重点来看看这两个类的区别。
两个GeneratedAppGlideModuleImpl
- 主模块
me.xcyoung.study
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
private final MyAppModule appGlideModule;
public GeneratedAppGlideModuleImpl(Context context) {
appGlideModule = new MyAppModule();
if (Log.isLoggable("Glide", Log.DEBUG)) {
Log.d("Glide", "Discovered AppGlideModule from annotation: me.xcyoung.study.MyAppModule");
}
}
复制代码
- lib模块
me.xcyoung.study.lib.test
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
private final MyLibModule appGlideModule;
public GeneratedAppGlideModuleImpl(Context context) {
appGlideModule = new MyLibModule();
if (Log.isLoggable("Glide", Log.DEBUG)) {
Log.d("Glide", "Discovered AppGlideModule from annotation: me.xcyoung.lib.test.MyLibModule");
}
}
复制代码
简单对比可以发现,生成包名相同的两个GeneratedAppGlideModuleImpl
,区别点在于其持有的AppGlideModule
子类不相同,即一个是在主模块定义的,一个是在lib模块定义的。
而仔细查看编译的apk包内可以发现,两个GeneratedAppGlideModuleImpl.class
存在于不同的dex文件中。
再结合之前的getAnnotationGeneratedGlideModules
方法不难看出,本文遇到的问题是JVM加载时找到了其中一个GeneratedAppGlideModuleImpl
,从而导致了后续的崩溃问题。
触发崩溃的位置
崩溃的点:
com.xxx.xxx.GlideRequests can not cast to com.xxx.xxx.GlideRequests
这两个GlideRequests
类分别在两个模块中,包名对应的是各自模块的包名,其父类为RequestManager
。而触发该异常的位置在于在加载图片前需要获取一个RequestManager
的对象(ps:GlideRequests
的父类)。这里以FragmentActivity
在载的情况为例。
@NonNull
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
// Tag 1
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
// Tag 2
public interface RequestManagerFactory {
@NonNull
RequestManager build(
@NonNull Glide glide,
@NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode requestManagerTreeNode,
@NonNull Context context);
}
复制代码
这里会通过RequestManagerFactory#build
实例一个RequestManager对象。在模拟代码及注解生成代码
一节中的注解生成代码中,GeneratedRequestManagerFactory
就是RequestManagerFactory子类。ps:注意这里也是存在两个GeneratedRequestManagerFactory
。
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
// annotationGeneratedModule即GeneratedAppGlideModuleImpl
...
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory()
: null;
builder.setRequestManagerFactory(factory);
...
}
复制代码
上述代码中annotationGeneratedModule
即GeneratedAppGlideModuleImpl
。在Glide初始化中,会通过GeneratedAppGlideModuleImpl
获取到GeneratedRequestManagerFactory
并设置到Glide对象中。
我们来看看主模块中生成的GeneratedRequestManagerFactory
:
import me.xcyoung.study.GlideRequests;
/**
* Generated code, do not modify
*/
final class GeneratedRequestManagerFactory implements RequestManagerRetriever.RequestManagerFactory {
@Override
@NonNull
public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle,
@NonNull RequestManagerTreeNode treeNode, @NonNull Context context) {
return new GlideRequests(glide, lifecycle, treeNode, context);
}
}
复制代码
这里返回的是主模块的GlideRequests
对象,即RequestManager。
总结
到这里,整个崩溃问题的出现也就呼之欲出了。
- 由于两个模块同时实现了AppGlideModule,导致了生成两个相同包名的
GeneratedAppGlideModuleImpl
和GeneratedRequestManagerFactory
。 - 两个模块的
GeneratedAppGlideModuleImpl
和GeneratedRequestManagerFactory
都包含各自模块的代码逻辑,包括自定义AppGlideModule以及各自生成的GlideRequests类等。 - 在Glide初始化时,通过反射获取
GeneratedAppGlideModuleImpl
,这时JVM从两个中获取最先出现的一个。譬如获取到主模块的GeneratedAppGlideModuleImpl。所以在后续操作中,一切逻辑都会根据主模块的定义。 - 在lib模块加载图片时,由于需要获取RequestManager,所以会调用到
GeneratedRequestManagerFactory#build
方法,理应获取到的是lib模块的GlideReuqests(me.xcyoung.lib.test.GlideRequests
)。但由于上述3中获取的是主模块的,所以获取到的是主模块的GlideRequests(me.xcyoung.study.GlideRequests
)。 - 在使用
GlideApp
加载图片时,需要获取自己模块的GlideRequests
对象。结合上述几点的分析,最终就造成强转失败的异常。
最后
本文主要分析了为什么Glide4.x的AppGlideModule只能在Application级别模块定义的问题。重点分析涉及到Glide的设计问题。总而言之,使用第三方库还是要根据官方文档,并自己加以理解。否则就会造成类似的不良问题。
参考文章: