图片框架 - Glide自定义配置和组件及Registry机制

接上篇,代码依然是4.11.0版本。

一、自定义配置和组件

1.1 Glide如何实现自定义配置和组件
/**
 * GlideModule注册方式:
 * 1)在application中加入:
 *  <meta-data
 *      android:name="xxx.xxx.xxx.glide.configure.CustomGlideMoudle"
 *      android:value="GlideModule" />
 * 2)@GlideModule注解
 */
@GlideModule
public final class CustomAppGlideModule extends AppGlideModule {

  //自定义配置
   @Override
   public void applyOptions(Context context, GlideBuilder builder) {
   这里主要配置GlideBuilder
      常用设置项:
      setMemoryCache() //用于配置Glide的内存缓存策略,默认配置是LruResourceCache。
      setBitmapPool()//用于配置Glide的Bitmap缓存池,默认配置是LruBitmapPool。
      setDiskCache()//用于配置Glide的硬盘缓存策略,默认配置是InternalCacheDiskCacheFactory。
      setDiskCacheService()//用于配置Glide读取缓存中图片的异步执行器,默认配置是FifoPriorityThreadPoolExecutor,也就是先入先出原则。
      setResizeService()//用于配置Glide读取非缓存中图片的异步执行器,默认配置也是FifoPriorityThreadPoolExecutor。
      setDecodeFormat()//用于配置Glide加载图片的解码模式,默认配置是RGB_565。
    }

/**
* Glide V4 版本,禁用清单解析选项
*/
@Override
public boolean isManifestParsingEnabled() {
    return false;

}

//自定义组件
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
这里通过Registry对组件进行注册
append 尾部追加
prepend 头部插入
register 注册、相当于append
replace 替换掉相同条件的模块
     }
}
1.2 自定义配置和组件的调用流程

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

看 getAnnotationGeneratedGlideModules(context.getApplicationContext()):

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) {
  ... 
 }
  return result;
}

它本身就是通过反射来使用com.bumptech.glide.GeneratedAppGlideModuleImpl 这个类。找下这个类:

很明显是由APT生成的,既然是APT,那就去找对应生成文件的Porcessor

对应的Glide引库为:annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

com.bumptech.glide.GeneratedAppGlideModuleImpl生成的主Processor在:

@AutoService(Processor.class)
public final class GlideAnnotationProcessor extends AbstractProcessor {
  static final boolean DEBUG = false;
  private ProcessorUtil processorUtil;
  private LibraryModuleProcessor libraryModuleProcessor;
  private AppModuleProcessor appModuleProcessor;
  private boolean isGeneratedAppGlideModuleWritten;
  private ExtensionProcessor extensionProcessor;

  @Override
  public synchronized void init(ProcessingEnvironment processingEnvironment) {
    super.init(processingEnvironment);
   processorUtil = new ProcessorUtil(processingEnvironment);
   IndexerGenerator indexerGenerator = new IndexerGenerator(processorUtil);
   libraryModuleProcessor = new LibraryModuleProcessor(processorUtil, indexerGenerator);
   appModuleProcessor = new AppModuleProcessor(processingEnvironment, processorUtil);
   extensionProcessor =
        new ExtensionProcessor(processingEnvironment, processorUtil, indexerGenerator);
  }

  @Override
  public Set<String> getSupportedAnnotationTypes() {
    Set<String> result = new HashSet<>();
   result.addAll(libraryModuleProcessor.getSupportedAnnotationTypes());
   result.addAll(extensionProcessor.getSupportedAnnotationTypes());
   return result;
  }

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
  }

  @Override
  public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
    processorUtil.process();
   boolean newModulesWritten = libraryModuleProcessor.processModules(env);
   boolean newExtensionWritten = extensionProcessor.processExtensions(env);
   appModuleProcessor.processModules(set, env);
   if (newExtensionWritten || newModulesWritten) {
      if (isGeneratedAppGlideModuleWritten) {
        throw new IllegalStateException("Cannot process annotations after writing AppGlideModule");
     }
      return true;
   }

    if (!isGeneratedAppGlideModuleWritten) {
      isGeneratedAppGlideModuleWritten = appModuleProcessor.maybeWriteAppModule();
   }
    return true;
  }
}

不了解编译时注解的可以参考之前的文章:Java基础(二)-注解

运行时注解简而言之就是在编译器生成一个类文件,类的内容通过javapoet组装出来。这个不是本文的重点,点到为止,那么来看看

生成后的内容:

@SuppressWarnings("deprecation")
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
  private final CustomAppGlideModule appGlideModule;
  GeneratedAppGlideModuleImpl() {
    //CustomAppGlideModule 实现了AppGlideModule,并且由@GlideModule方式注册
    appGlideModule = new CustomAppGlideModule();
   if (Log.isLoggable("Glide", Log.DEBUG)) {
      Log.d("Glide", "Discovered AppGlideModule from annotation: com.mgtv.lib.tv.imageloader.CustomAppGlideModule");
     Log.d("Glide", "Discovered LibraryGlideModule from annotation: com.bumptech.glide.integration.webp.WebpGlideLibraryModule");
   }
  }

  @Override
  public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
    appGlideModule.applyOptions(context, builder);
  }

  @Override
  public void registerComponents(@NonNull Context context, @NonNull Glide glide,
     @NonNull Registry registry) {
    //WebpGlideLibraryModule 实现了LibraryGlideModule,并且由@GlideModule方式注册
    new WebpGlideLibraryModule().registerComponents(context, glide, registry);
   appGlideModule.registerComponents(context, glide, registry);
  }

  @Override
  public boolean isManifestParsingEnabled() {
    return appGlideModule.isManifestParsingEnabled();
  }

  @Override
  @NonNull
  public Set<Class<?>> getExcludedModuleClasses() {
    return Collections.emptySet();
  }

  @Override
  @NonNull
  GeneratedRequestManagerFactory getRequestManagerFactory() {
    return new GeneratedRequestManagerFactory();
  }
}

这里总结下本地的配置在GeneratedAppGlideModuleImpl生成的规则:

  • AppGlideModule 只能实现1个,但是LibraryGlideModule可以实现多个。LibraryGlideModule只有registerComponents自定义组件功能,而AppGlideModule在LibraryGlideModule基础上增加了applyOptions自定义配置功能

  • 如果是@GlideModule注册,则会直接在GeneratedAppGlideModuleImpl创建对象,并执行相应方法,如果是manifest注册,则会到Glide初始化流程去进行处理。

好的,现在Glide反射的GeneratedAppGlideModuleImpl以及了解了,那么接下来看看使用:

Glide.java

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

接着看 checkAndInitializeGlide(context, annotationGeneratedModule),经过层层调用,最终:

private static void initializeGlide(
    @NonNull Context context,
   @NonNull GlideBuilder builder,
   @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
  Context applicationContext = context.getApplicationContext();
  List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
  if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
    manifestModules = new ManifestParser(applicationContext).parse();
  }

  //移除需要排除的GlideModule 
  ...
  for (com.bumptech.glide.module.GlideModule module : manifestModules) {
    module.applyOptions(applicationContext, builder);
  }

  //针对manifest 和 注解两种注册方式分别调用其applyOptions和registerComponents
  if (annotationGeneratedModule != null) {
    annotationGeneratedModule.applyOptions(applicationContext, builder);
  }

  //通过GlideBuilder做一系列初始化工作
  Glide glide = builder.build(applicationContext);
  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);
   }
  }

  if (annotationGeneratedModule != null) {
    annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
  }

  applicationContext.registerComponentCallbacks(glide);
  Glide.glide = glide;
}

这里针对manifest 和 注解两种注册方式分别调用其applyOptions和registerComponents来触发自定义配置和组件。

二、Registry机制

前面介绍了,registerComponents中是通过Registry对组件进行注册的,这里简单了解下Registry机制。
Registry初始化时机是在Glide的构造方法中,并且在那会添加一批默认的组件。
Registry本身的意义是组件根据功能进行灵活挂载。它只是作为一个入口类,具体功能会由如下具体Registry来处理:

  • ModelLoaderRegistry 注册ModelLoader
  • EncoderRegistry 注册Encoder
  • ResourceDecoderRegistry 注册ResourceDecoder
  • ResourceEncoderRegistry 注册ResourceEncoder
  • DataRewinderRegistry 注册DataRewinder
  • TranscoderRegistry 注册ResourceTranscoder
  • ImageHeaderParserRegistry 注册ImageHeaderParser

相关类介绍:

  • ModelLoader 由ModelLoaderFactory创建,作用是经由内部类LoadData将复杂数据模型转通过DataFetcher转换成需要的DataClass。
  • Encoder 将通用T持久化到本地cache
  • ResourceDecoder 解码Resource
  • ResourceEncoder 将Bitmap和对应Drawable持久化到本地cache
  • DataRewinder 对流进行重置数据起点
  • ResourceTranscoder 对Resource进行转换
  • ImageHeaderParser 图片头解析

以替换网络请求OKHTTP为例来介绍:

Registry.java

registry.replace(
       GlideUrl.class,   //Class<Model> modelClass ,GlideUrl对应的是一种Key,表示http/https url的字符串包装器。
       InputStream.class, //Class<Data> dataClass, 数据类型
       new OkHttpUrlLoader.Factory() //ModelLoaderFactory<? extends Model, ? extends Data> factory)  初始化了OkHttpClient
modelLoaderRegistry.replace(modelClass, dataClass, factory);
);

ModelLoaderRegistry.java

    @NonNull Class<Model> modelClass,
   @NonNull Class<Data> dataClass,
   @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
  tearDown(multiModelLoaderFactory.replace(modelClass, dataClass, factory));//tearDown在OkHttpUrlLoader没做逻辑
  cache.clear();
}

MultiModelLoaderFactory

@NonNull
synchronized <Model, Data> List<ModelLoaderFactory<? extends Model, ? extends Data>> replace(
    @NonNull Class<Model> modelClass,
   @NonNull Class<Data> dataClass,
   @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
  List<ModelLoaderFactory<? extends Model, ? extends Data>> removed =
      remove(modelClass, dataClass);//删除以前的组件
  append(modelClass, dataClass, factory);//添加当前新的组件
  return removed;
}

synchronized <Model, Data> void append(
    @NonNull Class<Model> modelClass,
   @NonNull Class<Data> dataClass,
   @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory) {
  add(modelClass, dataClass, factory, /*append=*/ true);
}

private final List<Entry<?, ?>> entries = new ArrayList<>();
private <Model, Data> void add(
    @NonNull Class<Model> modelClass,
   @NonNull Class<Data> dataClass,
   @NonNull ModelLoaderFactory<? extends Model, ? extends Data> factory,
   boolean append) {
  Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);
  entries.add(append ? entries.size() : 0, entry);
}

最终以Entry的形式添加到MultiModelLoaderFactory的ArrayList中。

再举个例子:

registry..prepend(
    Registry.BUCKET_BITMAP, //String bucket : bitmap
    InputStream.class,//Class<Data> dataClass
    Bitmap.class,//Class<TResource> resourceClass
    new StreamAnimatedBitmapDecoder(bitmapDecoder) //ResourceDecoder<Data, TResource> decoder
    decoderRegistry.prepend(bucket, decoder, dataClass, resourceClass);//ResourceDecoderRegistry
);

最大区别是最后传入的是个ResourceDecoder。

ResourceDecoderRegistry.java

private final List<String> bucketPriorityList = new ArrayList<>();
private final Map<String, List<Entry<?, ?>>> decoders = new HashMap<>();
public synchronized <T, R> void prepend(
    @NonNull String bucket,
   @NonNull ResourceDecoder<T, R> decoder,
   @NonNull Class<T> dataClass,
   @NonNull Class<R> resourceClass) {
  getOrAddEntryList(bucket).add(0, new Entry<>(dataClass, resourceClass, decoder));
}

@NonNull
private synchronized List<Entry<?, ?>> getOrAddEntryList(@NonNull String bucket) {
  if (!bucketPriorityList.contains(bucket)) {
    // Add this unspecified bucket as a low priority bucket.
   bucketPriorityList.add(bucket);
  }

  List<Entry<?, ?>> entries = decoders.get(bucket);

  if (entries == null) {
    entries = new ArrayList<>();
   decoders.put(bucket, entries);
  }
  return entries;
}

最终以Entry的形式保留在ResourceDecoderRegistry的map中。

后续是如何用的,参考后面的文章。

参考:

https://blog.csdn.net/weixin_34368949/article/details/88032953

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351