關(guān)于Glide
Glide是一個(gè)快速高效的Android圖片加載庫(kù),注重于平滑的滾動(dòng)。Glide提供了易用的API,高性能、可擴(kuò)展的圖片解碼管道(decode pipeline),以及自動(dòng)的資源池技術(shù)。

Glide 支持拉取,解碼和展示視頻快照,圖片,和GIF動(dòng)畫。Glide的Api是如此的靈活,開發(fā)者甚至可以插入和替換成自己喜愛的任何網(wǎng)絡(luò)棧。默認(rèn)情況下,Glide使用的是一個(gè)定制化的基于HttpUrlConnection的棧,但同時(shí)也提供了與Google Volley和Square OkHttp快速集成的工具庫(kù)。
雖然Glide 的主要目標(biāo)是讓任何形式的圖片列表的滾動(dòng)盡可能地變得更快、更平滑,但實(shí)際上,Glide幾乎能滿足你對(duì)遠(yuǎn)程圖片的拉取/縮放/顯示的一切需求。
API
Glide 使用簡(jiǎn)明的流式語(yǔ)法API,這是一個(gè)非常棒的設(shè)計(jì),因?yàn)樗试S你在大部分情況下一行代碼搞定需求:
Glide.with(fragment)
.load(url)
.into(imageView);
Generated API
Glide v4 使用 注解處理器 (Annotation Processor) 來(lái)生成出一個(gè) API,在 Application 模塊中可使用該流式 API 一次性調(diào)用到 RequestBuilder, RequestOptions 和集成庫(kù)中所有的選項(xiàng)。
Generated API 模式的設(shè)計(jì)出于以下兩個(gè)目的:
- 集成庫(kù)可以為 Generated API 擴(kuò)展自定義選項(xiàng)。
- 在 Application 模塊中可將常用的選項(xiàng)組打包成一個(gè)選項(xiàng)在 Generated API 中使用
雖然以上所說(shuō)的工作均可以通過(guò)手動(dòng)創(chuàng)建 RequestOptions 子類的方式來(lái)完成,但想將它用好更具有挑戰(zhàn),并且降低了 API 使用的流暢性。
GlideApp.with(fragment)
.load(myUrl)
.placeholder(R.drawable.placeholder)
.fitCenter()
.into(imageView);
性能
Glide 充分考慮了Android圖片加載性能的兩個(gè)關(guān)鍵方面:
- 圖片解碼速度
- 解碼圖片帶來(lái)的資源壓力
為了讓用戶擁有良好的App使用體驗(yàn),圖片不僅要快速加載,而且還不能因?yàn)檫^(guò)多的主線程I/O或頻繁的垃圾回收導(dǎo)致頁(yè)面的閃爍和抖動(dòng)現(xiàn)象。
Glide使用了多個(gè)步驟來(lái)確保在Android上加載圖片盡可能的快速和平滑:
- 自動(dòng)、智能地下采樣(downsampling)和緩存(caching),以最小化存儲(chǔ)開銷和解碼次數(shù);
- 積極的資源重用,例如字節(jié)數(shù)組和Bitmap,以最小化昂貴的垃圾回收和堆碎片影響;
- 深度的生命周期集成,以確保僅優(yōu)先處理活躍的Fragment和Activity的請(qǐng)求,并有利于應(yīng)用在必要時(shí)釋放資源以避免在后臺(tái)時(shí)被殺掉。
結(jié)構(gòu)

Glide構(gòu)造
-
Glide.with()
作用:初始化glide,返回一個(gè)RequestManger對(duì)象
以傳入的是FragmentActivity為例,getRetriever (activity)流程如下:

initializeGlide主要做了以下工作:
- 獲取應(yīng)用中帶注解的GlideModule(annotationGeneratedModule),這里要解釋一下GlideModule:用戶自定義glide配置模塊,用來(lái)修改默認(rèn)的glide配置信息。如果這個(gè)為空或者可配置menifest里面的標(biāo)志為true,則獲取menifest里面配置的GlideModule模塊(manifestModules)。
- 把manifestModules以及annotationGeneratedModule里面的配置信息放到builder里面(applyOptions)替換glide默認(rèn)組件(registerComponents)
各種初始化信息Glide glide = builder.build(applicationContext);
看一下build的源碼,主要做了以下工作: - 創(chuàng)建請(qǐng)求圖片線程池sourceExecutor,創(chuàng)建硬盤緩存線程池diskCacheExecutor。動(dòng)畫線程池animationExecutor
- 依據(jù)設(shè)備的屏幕密度和尺寸設(shè)置各種pool的size
- 創(chuàng)建圖片線程池LruBitmapPool,緩存所有被釋放的bitmap, LruBitmapPool依賴默認(rèn)的緩存策略和緩存配置。緩存策略在API大于19時(shí),為SizeConfigStrategy,小于為AttributeStrategy。其中SizeConfigStrategy是以bitmap的size和config為key,value為bitmap的HashMap
- 創(chuàng)建對(duì)象數(shù)組緩存池LruArrayPool,默認(rèn)4M
- 創(chuàng)建LruResourceCache,內(nèi)存緩存
- new glide里面 new Registry()注冊(cè)管理任務(wù)執(zhí)行對(duì)象的類(Registry),可以簡(jiǎn)單理解為:Registry是一個(gè)工廠,而其中所有注冊(cè)的對(duì)象都是一個(gè)工廠員工,當(dāng)任務(wù)分發(fā)時(shí),根據(jù)當(dāng)前任務(wù)的性質(zhì),分發(fā)給相應(yīng)員工進(jìn)行處理
先看代碼
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
可以看到,with方法有很多,但內(nèi)容基本一致,都是通過(guò) RequestManagerRetriever.get(); 獲取一個(gè) RequestManagerRetriever 對(duì)象 retriever ,然后通過(guò) retriever.get(context); 獲取一個(gè) RequestManager 對(duì)象并返回。這些with方法關(guān)鍵的不同在于傳入的參數(shù)不一致,可以是Context、Activity、Fragment等等。那么為什么要分這么多種呢?其實(shí)我們應(yīng)該都知道:Glide在加載圖片的時(shí)候會(huì)綁定 with(context) 方法中傳入的 context 的生命周期,如果傳入的是 Activity ,那么在這個(gè) Activity 銷毀的時(shí)候Glide會(huì)停止圖片的加載。這樣做的好處是顯而易見的:避免了消耗多余的資源,也避免了在Activity銷毀之后加載圖片從而導(dǎo)致的空指針問(wèn)題。
初始化glide,單例模式
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
防止重復(fù)初始化
private static void checkAndInitializeGlide(@NonNull Context context) {
// In the thread running initGlide(), one or more classes may call Glide.get(context).
// Without this check, those calls could trigger infinite recursion.
if (isInitializing) {
throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
+ " use the provided Glide instance instead");
}
isInitializing = true;
initializeGlide(context);
isInitializing = false;
}
加載自定義的配置例如自定義的modeloader,網(wǎng)絡(luò)加載棧等,注冊(cè)ComponentCallbacks,在低內(nèi)存時(shí) memoryCache, bitmapPool ,arrayPool 釋放到內(nèi)存
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;
}
構(gòu)建Glide,配置數(shù)據(jù)轉(zhuǎn)換器/解碼器/轉(zhuǎn)碼器/編碼器
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();
// 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.
// Note that order here matters. We want to check the ExifInterface parser first for orientation
// and then fall back to DefaultImageHeaderParser for other fields.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
registry.register(new ExifInterfaceImageHeaderParser());
}
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);
}
模型轉(zhuǎn)換器
| 轉(zhuǎn)換器 | 功能 |
|---|---|
| ResourceLoader.StreamFactory | 將Android資源ID轉(zhuǎn)換為Uri,在加載成為InputStream |
| ResourceLoader.UriFactory | 將資源ID轉(zhuǎn)換為Uri |
| ResourceLoader.FileDescriptorFactory | 將資源ID轉(zhuǎn)化為ParcelFileDescriptor |
| ResourceLoader.AssetFileDescriptorFactory | 將資源ID轉(zhuǎn)化為AssetFileDescriptor |
| UnitModelLoader.Factory | 不做任何轉(zhuǎn)換,返回源數(shù)據(jù) |
| ByteBufferFileLoader.Factory | 將File轉(zhuǎn)換為ByteBuffer |
| FileLoader.StreamFactory | 將File轉(zhuǎn)換為InputStream |
| FileLoader.FileDescriptorFactory | 將File轉(zhuǎn)化為ParcelFileDescriptor |
| DataUrlLoader.StreamFactory | 將Url轉(zhuǎn)化為InputStream |
| StringLoader.StreamFactory | 將String轉(zhuǎn)換為InputStream |
| StringLoader.AssetFileDescriptorFactory | 將String轉(zhuǎn)換為AssetFileDescriptor |
| HttpUriLoader.Factory | 將http/https Uri轉(zhuǎn)換為InputStream |
| UriLoader.StreamFactory | 將Uri轉(zhuǎn)換為InputStream |
| UriLoader.FileDescriptorFactory | 將Uri轉(zhuǎn)換為ParcelFileDescriptor |
| UriLoader.AssetFileDescriptorFactory | 將Uri轉(zhuǎn)換為AssetFileDescriptor |
| UrlUriLoader.StreamFactory | 將將http/https的Uri轉(zhuǎn)換為InputStream |
| UrlLoader.StreamFactory | 將Url轉(zhuǎn)換為InputStream |
| HttpGlideUrlLoader.Factory | 將HttpGlide轉(zhuǎn)換為InputStream |
解碼器
| 解碼器 | 功能 |
|---|---|
| ByteBufferGifDecoder | 將ByteBuffer解碼為GifDrawable |
| ByteBufferBitmapDecoder | 將ByteBuffer解碼為Bitmap |
| ResourceDrawableDecoder | 將資源Uri解碼為Drawable |
| ResourceBitmapDecoder | 將資源ID解碼為Bitmap |
| BitmapDrawableDecoder | 將數(shù)據(jù)解碼為BitmapDrawable |
| StreamBitmapDecoder | 將InputStreams解碼為Bitmap |
| StreamGifDecoder | 將InputStream數(shù)據(jù)轉(zhuǎn)換為BtyeBuffer,再解碼為GifDrawable |
| GifFrameResourceDecoder | 解碼gif幀 |
| FileDecoder | 包裝File成為FileResource |
| UnitDrawableDecoder | 將Drawable包裝為DrawableResource |
| UnitBitmapDecoder | 包裝Bitmap成為BitmapResource |
| VideoDecoder | 將本地視頻文件解碼為Bitmap |
編碼器
| 編碼器 | 功能 |
|---|---|
| ByteBufferEncoder | 將Byte數(shù)據(jù)緩存為File |
| StreamEncoder | InputStream緩存為File |
| BitmapEncoder | 將Bitmap數(shù)據(jù)緩存為File |
| BitmapDrawableEncoder | 將BitmapDrawable數(shù)據(jù)緩存為File |
| GifDrawableEncoder | 將GifDrawable數(shù)據(jù)緩存為File |
Glide的加載流程可以概括為以下流程:
model(數(shù)據(jù)源)-->data(轉(zhuǎn)換數(shù)據(jù))-->decode(解碼)-->transformed(縮放)-->transcoded(轉(zhuǎn)碼)-->encoded(編碼保存到本地)
Glide 生命周期管理
- ComponentCallbacks
public interface ComponentCallbacks2{
//當(dāng)?shù)蛢?nèi)存時(shí)回調(diào),
void onTrimMemory(int );
}
當(dāng)回調(diào)onTrimMemory(int level)執(zhí)行以下代碼,
釋放 memoryCache bitmapPool arrayPool 一些對(duì)象
public void trimMemory(int level) {
// Engine asserts this anyway when removing resources, fail faster and consistently
Util.assertMainThread();
// memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687.
memoryCache.trimMemory(level);
bitmapPool.trimMemory(level);
arrayPool.trimMemory(level);
}
- 與activity生命周期綁定,在Activity 添加一個(gè)不可見的RequestManagerFragment,監(jiān)聽其生命周期來(lái)決定當(dāng)前加載任務(wù)需不需要取消
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
- 如何加載GIF 文件
注意看GifFrameLoader,注意邏輯在這里,用handle不斷l(xiāng)oadNextFrame;
SourceGenerator-->HttpUrlFetcher-->inputstream (網(wǎng)絡(luò)獲取數(shù)據(jù))-> lrudiskcache 保存到本地-->DataCacheGenerator-->ByteBufferFetcher-->ByteBuffer-->byteBufferGifDecoder-->GifDrawableResource-->GifDrawable --> GifFrameLoader (這里面就開始循壞顯示圖片)


