最近在解決一些問題時,進行了一下檢索,發現
絕大多數文章
是
基于3.X
。Glide從進入4.X也有兩三年了,在3.X的基礎上,發生了很多變化。 所幸我對
4.X
的源碼還比較熟,且Glide的設計也很精彩,索性寫一寫
對4.X的剖析
。
當然,對于多數讀者而言,因為有一定的基礎知識,這些剖析文章并不是滿地金磚了,可以泛讀查漏。
三思系列是我最新的學習、總結形式,着重于:問題分析、技術積累、視野拓展,了解更多
本文主旨
Glide是一個
龐大
的項目,這一篇旨在對Glide項目4.X版本進行
全方位的認知
具體為:了解Glide處理哪些問題,哪些子產品與之相對應
對早年間 Square公司開源的
Picasso
比較熟悉的讀者而言,不難了解:
Picasso
是一個
小而雅
的架構,聚焦于圖檔加載的以下4點主要問題:
-
異步過程管理與排程
-
資源與顯示容器多對多關系
-
多級緩存
-
圖檔轉換支援ScaleType
而後起之秀
Glide
已經是一個
博大且細緻
的圖檔異步加載架構。
在Glide 1.X 時期,兩者的關注點基本一緻,随着官方支援Glide的發展,它的關注點也越來越多,包括但不限于:
-
資源的封裝
-
資源擷取方式與過程
-
緩存
-
、decode
的過程encode
-
,注:媒體資源擷取的最終目标,例如存為檔案、加載到ImageView中等Target的抽象
-
圖檔轉換
-
資源與Target多對多關系
-
生命周期感覺與控制
仔細思考這些點之後,我們不難發現,Glide已經着眼于解決這樣一個問題:
建構一個系統,對指定的辨別媒體資源的實體,其封裝形式可以自行擴充,執行資源的擷取,并應用記憶體、磁盤進行緩存,封裝圖檔的解碼過程,按指定要求進行
裁切、縮放等圖檔轉換,最終将結果運用到指定的目标。并對整個過程按照Android平台特性進行生命周期管理。
而這個過程中的絕大多數
節點
或者稱之為
切面
,均可進行
外部擴充
這一點我們可以從項目介紹中得到印證:看重點單詞即可
Glide is a fast and efficient open sourceand
media management
for Android that wraps
image loading framework
,
media decoding
, and
memory and disk caching
resource pooling
into a simple and easy to use interface.
Glide supports
,
fetching
, and
decoding
displaying
video stills, images, and animated GIFs. Glide includes a flexible API
that allows developers to plug in to almost any network stack. By default Glide uses a custom
HttpUrlConnection
based
stack, but also includes utility libraries plug in to Google’s Volley project or Square’s OkHttp library instead.
Glide’s
is on
primary focus
as
making scrolling any kind of a list of images
smooth and fast
as possible, but Glide is
also effective for
where you need to
almost any case
,
fetch
, and
resize
a remote image.
display
子產品職責劃分,從Glide的建構過程窺一斑
不難了解:一個龐大的系統需要在符合
SRP
的基礎上進行職責劃分,這樣一來,類就會變得很多,建構過程會變得複雜,利用Builder模式可以很好的解決這一問題。
class GlideBuilder {
public 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(),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock(),
defaultTransitionOptions);
}
}
從這裡我們可以收集到一下有效資訊
- 緩存與池
- MemorySizeCalculator
- LruBitmapPool
- BitmapPoolAdapter
- LruArrayPool
- LruResourceCache
- InternalCacheDiskCacheFactory
- 資源擷取
- RequestManagerRetriever
- DefaultConnectivityMonitorFactory
- 核心流程管理
- Engine
- 線程池
- GlideExecutor
再看到構造函數:代碼挺長,略去了部分内容
class 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 RequestOptions defaultRequestOptions,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
final Resources resources = context.getResources();
registry = new Registry();
registry.register(new DefaultImageHeaderParser());
Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
resources.getDisplayMetrics(), bitmapPool, arrayPool);
ByteBufferGifDecoder byteBufferGifDecoder =
new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);
ResourceDecoder<ParcelFileDescriptor, Bitmap> parcelFileDescriptorVideoDecoder =
VideoDecoder.parcel(bitmapPool);
ByteBufferBitmapDecoder byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
StreamBitmapDecoder streamBitmapDecoder = new StreamBitmapDecoder(downsampler, 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())
/*略去一段内容,太長了*/
.register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptions,
defaultTransitionOptions,
engine,
logLevel);
}
}
這一段可以分為即可部分:
- builder 的 field 對應取值指派
- 建構非開放的
執行個體BitmapPreFiller
- 建構
執行個體Registry
registry
- 建構
、DefaultImageHeaderParser
、ResourceLoader
、Decoder
并将之注冊到ResourceTranscoder
registry
- 建構
執行個體GlideContext
glideContext
按照經驗推斷,Registry 是一個政策系統資料庫,服務于
政策模式
。
不難了解:Glide的工作環境非常複雜,例如擷取資源,其資源存在位置可能在網絡中的某個位置、 可能在應用的Assets中,也可能在其他位置,對于某一個目标,根據場景不同,存在不同的實作政策
繼續按照經驗推斷,注冊會分為不同的政策組,對應不同的目标,這一點我們稍後再看。
再按照我們常見的使用習慣,追蹤一下初始化過程:泛讀即可
class Glide {
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
private static void checkAndInitializeGlide(@NonNull Context context) {
//...
initializeGlide(context);
//...
}
//重載略去
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
@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.with(...)
開始得到
RequestManager
,當然,也可以以
Glide.get(context).getRequestManagerRetriever()
方式擷取。如果Glide沒有初始化,則會調用
initializeGlide
進行初始化。
跟進一下代碼,泛讀即可
class Glide {
private static void initializeGlide(@NonNull Context context) {
initializeGlide(context, new GlideBuilder());
}
@SuppressWarnings("deprecation")
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
manifestModules = new ManifestParser(applicationContext).parse();
}
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());
}
}
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory() : null;
builder.setRequestManagerFactory(factory);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.registerComponents(applicationContext, glide, glide.registry);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
}
泛讀之後,我們發現主要有
三步
:
- 建構GlideBuilder
- 通過反射,加載
的自定義子產品,設定到Builder中可能存在
- 建構Glide
思危:如果直接使用GlideBuilder進行建構,則無法直接運用自定義子產品的内容,需要自行注入。
子產品職責劃分,于Register挖細節
上一節中,我們略去了Glide執行個體化過程的一些細節,其實作内容為:向Register注冊了各種子產品,可以泛讀一下源碼:
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)
.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 */
.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 */
.append(
Registry.BUCKET_GIF,
InputStream.class,
GifDrawable.class,
new StreamGifDecoder(registry.getImageHeaderParsers(),byteBufferGifDecoder,arrayPool))
.append(Registry.BUCKET_GIF,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))
.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())
.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 HttpUriLoader.Factory())
.append(Uri.class,InputStream.class,new AssetUriLoader.StreamFactory(context.getAssets()))
.append(
Uri.class,
ParcelFileDescriptor.class,
new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class,InputStream.class,new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class,InputStream.class,new MediaStoreVideoThumbLoader.Factory(context))
.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 */
.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);
直接講解
這段代碼是
無意義
的,我們從
Register
類入手,分析注冊的内容。
public class Registry {
public static final String BUCKET_GIF = "Gif";
public static final String BUCKET_BITMAP = "Bitmap";
public static final String BUCKET_BITMAP_DRAWABLE = "BitmapDrawable";
private static final String BUCKET_PREPEND_ALL = "legacy_prepend_all";
private static final String BUCKET_APPEND_ALL = "legacy_append";
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;
private final ModelToResourceClassCache modelToResourceClassCache =
new ModelToResourceClassCache();
private final LoadPathCache loadPathCache = new LoadPathCache();
private final Pool<List<Throwable>> throwableListPool = FactoryPools.threadSafeList();
public Registry() {
this.modelLoaderRegistry = new ModelLoaderRegistry(throwableListPool);
this.encoderRegistry = new EncoderRegistry();
this.decoderRegistry = new ResourceDecoderRegistry();
this.resourceEncoderRegistry = new ResourceEncoderRegistry();
this.dataRewinderRegistry = new DataRewinderRegistry();
this.transcoderRegistry = new TranscoderRegistry();
this.imageHeaderParserRegistry = new ImageHeaderParserRegistry();
setResourceDecoderBucketPriorityList(
Arrays.asList(BUCKET_GIF, BUCKET_BITMAP, BUCKET_BITMAP_DRAWABLE));
}
//API略去
}
資源Model加載器
modelLoaderRegistry
用于注冊
資源Model加載方式
資訊。例如,資源的定義Model為
Uri
類,注冊一個
ModelLoaderFactory
,生産對應的
ModelLoader
,将
Uri
媒體實體,加載為
InputStream
或者其他形式的
内容
相關類:
-
ModelLoaderFactory
-
等ModelLoader
編碼器
encoderRegistry
用于注冊
内容encode方式
資訊。例如,媒體實體已被加載為
InputStream
,注冊一個對應處理的
Encoder<InputStream>
,
用于處理内容的encode,如寫入磁盤過程
相關類:
Encoder
接口及其實作類
相應的,
resourceEncoderRegistry
用于注冊
Resource 的encode方式
,這些encoder面向
Resource
,相比于
InputStream
等低級形式的編碼内容封裝,
Resource
實作類進行了更進階别的編碼内容封裝。
相關類:
-
接口及其實作類ResourceEncoder
-
接口及其實作類Resource
-
EncodeStrategy
不難了解:媒體資源的資料封裝存在不同
層級
:
- 讀取資料的InputSteam
- Bitmap,BitmapDrawable
是以 encode過程 對不同層級的封裝類,有不同的處理,當然,encoder可以通過
wrapper
,
adapter
,
transfer
等形式,利用其它encoder擴充功能
解碼器
decoderRegistry
用于注冊
媒體資料解碼為Resource方式
的資訊。
前面我們提到
modelLoaderRegistry
注冊的資訊中,可以得到
ModelLoader
, 它們負責将
資源Model
加載為
資源内容
,這種
資源内容
以
InputStream
等低級形式存在即可。
不難了解,這可以有效的解決
類數量爆炸
問題。
腦暴:假定 Model 的類數量為m個,解碼後輸出的形式有n個當
- 不采用這種方式處理,一共需要 m*n 個 decoder 類
- 采用這種方式處理,一共需要 m+n 個 decoder 類
層級
增加時,差距會更加明顯
按照經驗推斷,Glide在處理decode過程時,采用了
模式 或者
Wrapper/delegate
模式。
bridge
思變:項目中是否可以使用類似方式,處理不同層級之間的
資料Bean
或者
轉化
?
包裝
相關類:
-
及其實作類ResourceDecoder
-
及其實作類Resource
轉換器
雖然直譯為轉碼器,但是容易概念混淆,按照其功能稱為轉換器
transcoderRegistry
用于注冊
轉化器,及其負責的原始類型和轉換類型
資訊,轉換器将資源從一種形式轉換為另一種形式。例如:
Bitmap -> byte[]
相關類:
-
及其實作類ResourceTranscoder
-
及其實作類Resource
頭資訊解析器
imageHeaderParserRegistry
用于注冊
頭資訊解析器
資訊
注,部分作業系統為了更簡便的決定檔案的處理方式,利用了檔案擴充名。但檔案實質内容的編碼方式資訊,存儲在檔案頭資訊中,又稱:
File Sigs
、
檔案魔數
。
例如 FF D8 代表 Generic JPEG image file,即 JPE, JPEG, JPG
了解更多關于File Sigs内容
相關類:
-
及其實作類ImageHeaderParser
-
ImageType
子產品職責總結
通過前文的分析,并查閱Glide源碼,我們可以對子產品職責進行一次初步總結。
在之後的文章中,我們将對具體子產品進行
更細緻
的挖掘,此處總結的類資訊,就是
突破口
。
考慮到目前收集的資訊
并不全面
,我不打算繪制一個圖,下面的資訊也
并非
十分重要,是以将其作為一個
臨時的備忘錄
即可。
在後續内容中,我會對各個子產品單獨總結成圖
下一篇,我們将對
資源Model加載、擷取流程
進行
深度挖掘
。
緩存與池
- MemorySizeCalculator
- LruBitmapPool
- BitmapPoolAdapter
- LruArrayPool
- LruResourceCache
- InternalCacheDiskCacheFactory
資源擷取
- RequestManagerRetriever
- RequestManager
- RequestBuilder
- Request
- DefaultConnectivityMonitorFactory
核心流程
- Engine
其他資訊尚不能确定。主要流程的相關資訊如下:
資源Model加載器
-
ModelLoaderFactory
-
等ModelLoader
編碼器
-
接口及其實作類Encoder
-
接口及其實作類ResourceEncoder
-
接口及其實作類Resource
-
EncodeStrategy
解碼器
-
及其實作類ResourceDecoder
-
及其實作類Resource
轉換器
-
及其實作類ResourceTranscoder
-
及其實作類Resource
頭資訊解析器
-
及其實作類ImageHeaderParser
-
ImageType
線程池
- GlideExecutor
Glide的建立與配置
GlideBuilder
用于建立,
com.bumptech.glide.module
包下的内容用于自定義子產品,諸如:
-
AppGlideModule
-
LibraryGlideModule
以及一些已廢棄的内容,不再贅述
思危、思退、思變
可能有讀者習慣性地拉到尾部,前面提到,結尾處的大篇幅内容都是
備忘錄
性質,而且之後的系列文章會在這些内容的基礎上展開,可讀可不讀
但既然拉到了這裡,就思考一下吧,閱讀優秀的架構,不僅僅可以深入了解架構内容,也可以鍛煉設計能力:
- 思危:使用GlideBuilder建構Glide執行個體是否有沒有注意到的危險?
- 思危:項目中再次對Glide實作單例是否存在危險?
- 思變:項目中是否存在可以借鑒Glide的設計思路的地方?