- 在整体流程中Glide对象的获得是通过
Glide.get(context)
, 那这个get过程都经历哪些初始化流程,做了哪些事情? - 如下代码,我们往往会更改一些自定义的配置,只是继承了抽象类
AppGlideModule
,并且添加注解@GlideModule
那这个配置是怎么生效的呢?
@GlideModule
public class EmagGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
ViewTarget.setTagId(R.id.glide_tag);
if (SystemCompat.getIns().isAndroidGo()) {
builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565)
.disallowHardwareConfig());
} else {
builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888));
}
builder.setMemoryCache(GlideParams.get().getMemoryCache());
builder.setBitmapPool(GlideParams.get().getBitmapPool());
builder.setDiskCache(GlideParams.get().getDiskCacheFactory());
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
// 替换为OKhttp
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
@Override
public boolean isManifestParsingEnabled() {
return false;
}
}
Glide#get(context)
public static Glide get(@NonNull Context context) {
if (glide == null) {
// 注释1,获取通过注解动态生成的的AppGlideModule的实现类
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
// 注释2, 检查并初始化Glide
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
- 采用DCL单例模式
- 注释1 , 获取通过注解动态生成的的AppGlideModule的实现类
- 注释2, 检查并初始化Glide
Glide#initializeGlide()
private static void initializeGlide(
@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
// 注释1 初始化Glide, 这里直接创建了 GlideBuilder
initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
}
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
// 注释2 从Manifest获取定义的GlideModule列表, 但有前提条件 且此种方式已标记过时
List<com.bumptech.glide.module.GlideModule> m anifestModules = Collections.emptyList();
//这里有个判断就是当注解动态生成是空,或者不为空但 isManifestParsingEnabled == true
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
manifestModules = new ManifestParser(applicationContext).parse();
}
// 注释3 从注解动态生成的的AppGlideModule的实现类中去掉Manifest定义的GlideMoldue
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());
}
}
// 注释4 实际上我们自定义的AppGlideModule中的Glide的配置,就是通过builder设置的
// 从44行到57行 就是关联配置
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory()
: null;
//注释5 设置RequestManager的工厂类
builder.setRequestManagerFactory(factory);
//注释6 将Manifest定义的GlideModule中的配置选项设置到glideBuilder中
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
//注释7 将注解动态生成的的AppGlideModule的实现类中的配置选项设置到glideBuilder中
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
// 注释8 【Builder设计模式】 创建完毕Glide
Glide glide = builder.build(applicationContext);
// 注释9 将Manifest定义的GlideModule中定义的Glide组件注册到Glide中
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);
}
}
// 注释10 将注解动态生成的的AppGlideModule的实现类中定义的Glide组件注册到Glide中
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
// 注释11 让glide监听Application的组件生命周期
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
通过代码分析,主要初始化流程如下:
- 注释1 准备初始化Glide, 这里直接创建了
GlideBuilder
- 注释2 从Manifest获取定义的GlideModule列表, 但有前提条件 且此种方式已标记过时
- 注释3 从注解动态生成的的AppGlideModule的实现类中去掉Manifest定义的GlideMoldue
- 注释4 实际上我们自定义的AppGlideModule中的Glide的配置,就是通过builder设置的,从44行到57行 实际就是关联配置
- 注释5 设置RequestManager的工厂类
- 注释6 将Manifest定义的GlideModule中的配置选项设置到glideBuilder中
- 注释7 将注解动态生成的的AppGlideModule的实现类中的配置选项设置到glideBuilder中
- 注释8 【Builder设计模式】 创建完毕Glide
- 注释9 将Manifest定义的GlideModule中定义的Glide组件注册到Glide中
- 注释10 将注解动态生成的的AppGlideModule的实现类中定义的Glide组件注册到Glide中
- 注释11 让glide监听Application的组件生命周期
这里可以回答开头问题之一,自定义的配置是生效时机
- 注释7 会调用 我们自定义的
EmagGlideModule#applyOptions()
- 注释10 会调用 我们自定义的
EmagGlideModule#registerComponents()
GildeBuilder#build()
对于构建者模式来创建对象已经很熟悉了,我们这里主要是看下GildeBuilder
都有哪些配置项
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();
}
// 创建bitmap复用池
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 requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory, experiments);
// 将上面的配置参数,构造函数传递给glide进而创建对象,这是一次标准的builder设计模式
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
experiments);
}
可见,如果外面没有指定的配置,那么都会创建默认的配置项,然后再创建Glide
对象,接着我们看下Glide
的构造函数又做了哪些事情
Glide#new Glide()
构造函数
Glide(
@NonNull Context context,
@NonNull Engine engine,
@NonNull MemoryCache memoryCache,
@NonNull BitmapPool bitmapPool,
@NonNull ArrayPool arrayPool,
@NonNull RequestManagerRetriever requestManagerRetriever,
@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
@NonNull RequestOptionsFactory defaultRequestOptionsFactory,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
@NonNull List<RequestListener<Object>> defaultRequestListeners,
GlideExperiments experiments) {
/* 正常属性赋值 */
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
this.defaultRequestOptionsFactory = defaultRequestOptionsFactory;
final Resources resources = context.getResources();
/* 注释1 创建注册表,Registry 管理组件注册以扩展或替换 Glide 的默认加载、解码和编码逻辑 */
registry = new Registry();
registry.register(new DefaultImageHeaderParser());
// Right now we're only using this parser for HEIF images, which are only supported on OMR1+.
// If we need this for other file types, we should consider removing this restriction.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
registry.register(new ExifInterfaceImageHeaderParser());
}
List<ImageHeaderParser> imageHeaderParsers = registry.getImageHeaderParsers();
ByteBufferGifDecoder byteBufferGifDecoder =
new ByteBufferGifDecoder(context, imageHeaderParsers, bitmapPool, arrayPool);
ResourceDecoder<ParcelFileDescriptor, Bitmap> parcelFileDescriptorVideoDecoder =
VideoDecoder.parcel(bitmapPool);
// TODO(judds): Make ParcelFileDescriptorBitmapDecoder work with ImageDecoder.
Downsampler downsampler =
new Downsampler(
registry.getImageHeaderParsers(), resources.getDisplayMetrics(), bitmapPool, arrayPool);
ResourceDecoder<ByteBuffer, Bitmap> byteBufferBitmapDecoder;
ResourceDecoder<InputStream, Bitmap> streamBitmapDecoder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& experiments.isEnabled(EnableImageDecoderForBitmaps.class)) {
streamBitmapDecoder = new InputStreamBitmapImageDecoderResourceDecoder();
byteBufferBitmapDecoder = new ByteBufferBitmapImageDecoderResourceDecoder();
} else {
byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& experiments.isEnabled(EnableImageDecoderForAnimatedWebp.class)) {
registry.append(
Registry.BUCKET_ANIMATION,
InputStream.class,
Drawable.class,
AnimatedWebpDecoder.streamDecoder(imageHeaderParsers, arrayPool));
registry.append(
Registry.BUCKET_ANIMATION,
ByteBuffer.class,
Drawable.class,
AnimatedWebpDecoder.byteBufferDecoder(imageHeaderParsers, arrayPool));
}
ResourceDrawableDecoder resourceDrawableDecoder = new ResourceDrawableDecoder(context);
ResourceLoader.StreamFactory resourceLoaderStreamFactory =
new ResourceLoader.StreamFactory(resources);
ResourceLoader.UriFactory resourceLoaderUriFactory = new ResourceLoader.UriFactory(resources);
ResourceLoader.FileDescriptorFactory resourceLoaderFileDescriptorFactory =
new ResourceLoader.FileDescriptorFactory(resources);
ResourceLoader.AssetFileDescriptorFactory resourceLoaderAssetFileDescriptorFactory =
new ResourceLoader.AssetFileDescriptorFactory(resources);
BitmapEncoder bitmapEncoder = new BitmapEncoder(arrayPool);
BitmapBytesTranscoder bitmapBytesTranscoder = new BitmapBytesTranscoder();
GifDrawableBytesTranscoder gifDrawableBytesTranscoder = new GifDrawableBytesTranscoder();
ContentResolver contentResolver = context.getContentResolver();
registry
.append(ByteBuffer.class, new ByteBufferEncoder())
.append(InputStream.class, new StreamEncoder(arrayPool))
/* Bitmaps */
.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
.append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder);
if (ParcelFileDescriptorRewinder.isSupported()) {
registry.append(Registry.BUCKET_BITMAP, ParcelFileDescriptor.class,
Bitmap.class, new ParcelFileDescriptorBitmapDecoder(downsampler));
}
registry
.append(Registry.BUCKET_BITMAP, ParcelFileDescriptor.class,
Bitmap.class,parcelFileDescriptorVideoDecoder)
.append(Registry.BUCKET_BITMAP, AssetFileDescriptor.class,
Bitmap.class, VideoDecoder.asset(bitmapPool))
.append(Bitmap.class, Bitmap.class, UnitModelLoader.Factory.<Bitmap>getInstance())
.append(Registry.BUCKET_BITMAP, Bitmap.class, Bitmap.class, new UnitBitmapDecoder())
.append(Bitmap.class, bitmapEncoder)
/* BitmapDrawables */
/* 注册各种转换成BitmapDrawable的图片解码器 */
.append(Registry.BUCKET_BITMAP_DRAWABLE, ByteBuffer.class,BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
.append(Registry.BUCKET_BITMAP_DRAWABLE, InputStream.class, BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
.append(Registry.BUCKET_BITMAP_DRAWABLE, ParcelFileDescriptor.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, parcelFileDescriptorVideoDecoder))
.append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder))
/* GIFs */
/* 注册各种转换成gif的图片解码器 */
.append(
Registry.BUCKET_ANIMATION,
InputStream.class,
GifDrawable.class,
new StreamGifDecoder(imageHeaderParsers, byteBufferGifDecoder, arrayPool))
.append(
Registry.BUCKET_ANIMATION, ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
.append(GifDrawable.class, new GifDrawableEncoder())
/* GIF Frames */
// Compilation with Gradle requires the type to be specified for UnitModelLoader here.
.append(
GifDecoder.class, GifDecoder.class, UnitModelLoader.Factory.<GifDecoder>getInstance())
.append(Registry.BUCKET_BITMAP, GifDecoder.class, Bitmap.class,
new GifFrameResourceDecoder(bitmapPool))
/* Drawables */
.append(Uri.class, Drawable.class, resourceDrawableDecoder)
.append(
Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitmapPool))
/* Files */
/* 注册各种解析图片文件的图片加载器和图片解码器 */
.register(new ByteBufferRewinder.Factory())
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, File.class, new FileDecoder())
.append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
// Compilation with Gradle requires the type to be specified for UnitModelLoader here.
.append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
/* Models */
.register(new InputStreamRewinder.Factory(arrayPool));
if (ParcelFileDescriptorRewinder.isSupported()) {
registry.register(new ParcelFileDescriptorRewinder.Factory());
}
registry
.append(int.class, InputStream.class, resourceLoaderStreamFactory)
.append(int.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)
.append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
.append(Integer.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)
.append(Integer.class, Uri.class, resourceLoaderUriFactory)
.append(int.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory)
.append(Integer.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory)
.append(int.class, Uri.class, resourceLoaderUriFactory)
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
.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(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(Uri.class, AssetFileDescriptor.class,
new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
registry.append(
Uri.class, InputStream.class, new QMediaStoreUriLoader.InputStreamFactory(context));
registry.append(
Uri.class,
ParcelFileDescriptor.class,
new QMediaStoreUriLoader.FileDescriptorFactory(context));
}
registry
.append(Uri.class, InputStream.class, new UriLoader.StreamFactory(contentResolver))
.append(
Uri.class,
ParcelFileDescriptor.class,
new UriLoader.FileDescriptorFactory(contentResolver))
.append(
Uri.class,
AssetFileDescriptor.class,
new UriLoader.AssetFileDescriptorFactory(contentResolver))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
.append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
.append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
.append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
.append(Uri.class, Uri.class, UnitModelLoader.Factory.<Uri>getInstance())
.append(Drawable.class, Drawable.class, UnitModelLoader.Factory.<Drawable>getInstance())
.append(Drawable.class, Drawable.class, new UnitDrawableDecoder())
/* Transcoders */
/* 注册将Bitmap转换成BitmapDrawable的转换器 */
.register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))
.register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
.register(
Drawable.class,
byte[].class,
new DrawableBytesTranscoder(
bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
.register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ResourceDecoder<ByteBuffer, Bitmap> byteBufferVideoDecoder =
VideoDecoder.byteBuffer(bitmapPool);
registry.append(ByteBuffer.class, Bitmap.class, byteBufferVideoDecoder);
registry.append( ByteBuffer.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, byteBufferVideoDecoder));
}
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
// 创建 GlideContext
glideContext =
new GlideContext(context, arrayPool, registry, imageViewTargetFactory,
defaultRequestOptionsFactory, defaultTransitionOptions,
defaultRequestListeners, engine, experiments, logLevel);
}
整个构造方法主要做了三件事
- 属性赋值,
GlideBuilder
传递过来的值赋值给glide的属性 -
注册表注册,
Registry
中包含各种注册表,通过append()
或者register()
主要包括7种注册表 - 创建
GlideContext
Registry#append()
和 Registry#register()
这些注册表封装在这两个方法供调用,这里主要简单熟悉下都有哪些注册表
public class Registry {
...
private final ModelLoaderRegistry modelLoaderRegistry;
private final EncoderRegistry encoderRegistry;
private final ResourceDecoderRegistry decoderRegistry;
private final ResourceEncoderRegistry resourceEncoderRegistry;
private final DataRewinderRegistry dataRewinderRegistry;
private final TranscoderRegistry transcoderRegistry;
private final ImageHeaderParserRegistry imageHeaderParserRegistry;
...
}
-
ModelLoaderRegistry
模型加载器注册表, 维护 ModelLoader 的有序放置以及它们处理的模型和数据类型,按从最高优先级到最低优先级的顺序排列。 -
EncoderRegistry
编码器注册表,包含能够编码任意数据类型的编码器的有序列表。 -
ResourceDecoderRegistry
资源解码器注册表,包含 ResourceDecoder 的有序列表,能够将任意数据类型解码为从最高优先级解码器到最低优先级解码器的任意资源类型。 -
ResourceEncoderRegistry
资源编码器注册表,包含能够编码任意资源类型的 ResourceEncoder 的有序列表。 -
DataRewinderRegistry
数据回卷器注册表,存储数据类到 DataRewinder.Factory 的映射,并允许注册新类型和工厂。 -
TranscoderRegistry
转码器注册表,一个类,它允许 ResourceTranscoders 被它们之间转换的类注册和检索。 -
ImageHeaderParserRegistry
图像头解析器注册表,包含能够解析图像标题的 ImageHeaderParsers 的无序列表。
小结
- 解析注解定义的AppGlideModule或者Manifest定义的GlideModule,将其对应的配置信息设置给GlideBuilder
- GlideBuilder设置一些默认的配置信息,比如网络线程池、磁盘线程池、动画线程池、内存缓存、磁盘缓存等
- GlideBuilder构建Glide,在创建Glide时注册了各种用于图片加载、解码、编码的图片加载器、图片解码器、图片编码器等
- 将AppGlideModule和GlideModule自定义的图片加载、解码器、编码器注册到Glide中
- 监听Application的生命周期
网友评论