圖片框架 - Glide自定義配置和組件及Registry機制

接上篇蝗敢,代碼依然是4.11.0版本逗堵。

一叶眉、自定義配置和組件

1.1 Glide如何實現(xiàn)自定義配置和組件
/**
 * 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
      常用設(shè)置項:
      setMemoryCache() //用于配置Glide的內(nèi)存緩存策略慷妙,默認配置是LruResourceCache僻焚。
      setBitmapPool()//用于配置Glide的Bitmap緩存池,默認配置是LruBitmapPool膝擂。
      setDiskCache()//用于配置Glide的硬盤緩存策略虑啤,默認配置是InternalCacheDiskCacheFactory。
      setDiskCacheService()//用于配置Glide讀取緩存中圖片的異步執(zhí)行器架馋,默認配置是FifoPriorityThreadPoolExecutor狞山,也就是先入先出原則。
      setResizeService()//用于配置Glide讀取非緩存中圖片的異步執(zhí)行器叉寂,默認配置也是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 自定義配置和組件的調(diào)用流程

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,那就去找對應(yīng)生成文件的Porcessor

對應(yīng)的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基礎(chǔ)(二)-注解

運行時注解簡而言之就是在編譯器生成一個類文件堤结,類的內(nèi)容通過javapoet組裝出來搏讶。這個不是本文的重點,點到為止霍殴,那么來看看

生成后的內(nèi)容:

@SuppressWarnings("deprecation")
final class GeneratedAppGlideModuleImpl extends GeneratedAppGlideModule {
  private final CustomAppGlideModule appGlideModule;
  GeneratedAppGlideModuleImpl() {
    //CustomAppGlideModule 實現(xiàn)了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 實現(xiàn)了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();
  }
}

這里總結(jié)下本地的配置在GeneratedAppGlideModuleImpl生成的規(guī)則:

  • AppGlideModule 只能實現(xiàn)1個来庭,但是LibraryGlideModule可以實現(xiàn)多個妒蔚。LibraryGlideModule只有registerComponents自定義組件功能,而AppGlideModule在LibraryGlideModule基礎(chǔ)上增加了applyOptions自定義配置功能

  • 如果是@GlideModule注冊月弛,則會直接在GeneratedAppGlideModuleImpl創(chuàng)建對象肴盏,并執(zhí)行相應(yīng)方法,如果是manifest注冊帽衙,則會到Glide初始化流程去進行處理菜皂。

好的,現(xiàn)在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)恍飘,經(jīng)過層層調(diào)用,最終:

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 和 注解兩種注冊方式分別調(diào)用其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 和 注解兩種注冊方式分別調(diào)用其applyOptions和registerComponents來觸發(fā)自定義配置和組件谴垫。

二章母、Registry機制

前面介紹了,registerComponents中是通過Registry對組件進行注冊的翩剪,這里簡單了解下Registry機制乳怎。
Registry初始化時機是在Glide的構(gòu)造方法中,并且在那會添加一批默認的組件前弯。
Registry本身的意義是組件根據(jù)功能進行靈活掛載蚪缀。它只是作為一個入口類,具體功能會由如下具體Registry來處理:

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

相關(guān)類介紹:

  • ModelLoader 由ModelLoaderFactory創(chuàng)建恕出,作用是經(jīng)由內(nèi)部類LoadData將復(fù)雜數(shù)據(jù)模型轉(zhuǎn)通過DataFetcher轉(zhuǎn)換成需要的DataClass询枚。
  • Encoder 將通用T持久化到本地cache
  • ResourceDecoder 解碼Resource
  • ResourceEncoder 將Bitmap和對應(yīng)Drawable持久化到本地cache
  • DataRewinder 對流進行重置數(shù)據(jù)起點
  • ResourceTranscoder 對Resource進行轉(zhuǎn)換
  • ImageHeaderParser 圖片頭解析

以替換網(wǎng)絡(luò)請求OKHTTP為例來介紹:

Registry.java

registry.replace(
       GlideUrl.class,   //Class<Model> modelClass ,GlideUrl對應(yīng)的是一種Key剃根,表示http/https url的字符串包裝器哩盲。
       InputStream.class, //Class<Data> dataClass, 數(shù)據(jù)類型
       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
);

最大區(qū)別是最后傳入的是個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中廉油。

后續(xù)是如何用的,參考后面的文章苗傅。

參考:

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載抒线,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末渣慕,一起剝皮案震驚了整個濱河市嘶炭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逊桦,老刑警劉巖眨猎,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異强经,居然都是意外死亡睡陪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門匿情,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兰迫,“玉大人,你說我怎么就攤上這事炬称≈” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵玲躯,是天一觀的道長据德。 經(jīng)常有香客問我,道長跷车,這世上最難降的妖魔是什么晋控? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮姓赤,結(jié)果婚禮上赡译,老公的妹妹穿的比我還像新娘。我一直安慰自己不铆,他們只是感情好蝌焚,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著誓斥,像睡著了一般只洒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上劳坑,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天毕谴,我揣著相機與錄音,去河邊找鬼。 笑死涝开,一個胖子當著我的面吹牛循帐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舀武,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拄养,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了银舱?” 一聲冷哼從身側(cè)響起瘪匿,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寻馏,沒想到半個月后棋弥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡诚欠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年顽染,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片聂薪。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡家乘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出藏澳,到底是詐尸還是另有隱情仁锯,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布翔悠,位于F島的核電站业崖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蓄愁。R本人自食惡果不足惜双炕,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望撮抓。 院中可真熱鬧妇斤,春花似錦、人聲如沸丹拯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乖酬。三九已至死相,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咬像,已是汗流浹背算撮。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工生宛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肮柜。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓陷舅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親素挽。 傳聞我的和親對象是個殘疾皇子蔑赘,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355