1、什么是Glide?
1.1、官方描述
Glide是一個快速高效的Android圖片加載庫,注重于平滑的滾動。Glide提供了易用的API,高性能、可擴展的圖片解碼管道(decode pipeline),以及自動的資源池技術。
- Glide 支持拉取,解碼和展示視頻快照、圖片和GIF動畫。Glide的Api靈活易用,開發者可以插入和替換成自己喜愛的任何網絡棧。默認情況下,Glide使用的是一個定制化的基于HttpUrlConnection的棧,但同時也提供了與Google Volley和Square OkHttp快速集成的工具庫。
- Glide 的目標是讓任何形式的圖片列表的滾動盡可能地變得更快、更平滑,但實際上,Glide幾乎能滿足你對遠程圖片的拉取/縮放/顯示的一切需求。
1.2、特點
- 自動、智能地采樣(downsampling)和緩存(caching),最小化存儲的開銷和解碼的次數;
- 有效的資源重用,例如字節數組和Bitmap,最小化垃圾回收和堆碎片的影響;
- 深度的生命周期集成,確保優先處理活躍的Fragment和Activity的請求,同時有利于應用在必要時釋放資源(頁面銷毀等)。
2、Glide怎么使用?
2.1、官方 Glide API
Glide 使用簡明的流式語法API,這是一個非常棒的設計,因為它允許你在大部分情況下一行代碼搞定需求:
// build.gradle文件添加 Glide 依賴
dependencies {
implementation "com.github.bumptech.glide:glide:4.12.0"
}
// API 簡單使用
Glide.with(context)
.load(url)
.into(imageView);
2.2、自定義 Glide API
通過Glide提供的注解,來添加自己定義的API
- GlideModule 注解用于AppGlideModule
- GlideExtension 注解用于標識一個擴展Glide API的類,任何拓展Glide API的類都必須使用這個注解來標記,被@GlideExtension注解的類應以工具類的方式實現。
- GlideOption 注解為RequestOptions添加一個選項。
- GlideType 添加新的資源類型的支持(GIF,SVG等)
項目需要通過GlideModule注解繼承自AppGlideModule類的子類,并通過GlideExtension注解到工具類上,來擴展自定義Glide API,使用GlideOption、GlideType注解時必須為靜態方法,最后經過Rebuild Project之后,最終會被編譯到XXXRequest.java類
// build.gradle文件添加 Glide 注解處理器
dependencies {
implementation "com.github.bumptech.glide:glide:4.12.0"
annotationProcessor "com.github.bumptech.glide:compiler:4.12.0"
}
// Application模塊內,GlideModule注解自定義子類繼承AppGlideModule,可以不用重寫任何方法。
@GlideModule(glideName = "GlideApp")
public class MyGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
// 可以添加一些全局性的options
super.applyOptions(context, builder);
}
}
// GlideExtension注解,添加自定義的Glide API
@GlideExtension
public class MyGlideExtensions {
private MyGlideExtensions() {
}
// GlideOption注解,添加自定義的Option
@GlideOption
public static BaseRequestOptions<?> myMiniThumb(BaseRequestOptions<?> options, int size) {
return options.fitCenter().override(size);
}
// 自定義decode resource Type
private static final RequestOptions DECODE_TYPE_GIF = RequestOptions.decodeTypeOf(GifDrawable.class);
// GlideType注解,添加自定義的資源類型
@GlideType(GifDrawable.class)
public static RequestBuilder<GifDrawable> asMyGif(RequestBuilder<GifDrawable> requestBuilder) {
return requestBuilder
.transition(new DrawableTransitionOptions()) // 設置用于在加載完成時從占位符到正常顯示的過渡效果
.apply(DECODE_TYPE_GIF); // 將自定義的ResourceClass設置到resourceClass參數
}
}
// 使用自定義API
GlideApp.with(context)
.asMyGif() // 使用自定義的資源
.load(url)
.myMiniThumb(100) // 使用自定義的Option
.into(view);
3、Glide加載圖片過程
Glide加載圖片的過程,可以分為三個階段:with、load和into。
Glide 結構圖如下:
3.1、with階段
with方法用于獲取到RequestManager,RequestManager用于管理圖片請求;在創建RequestManager時,根據不同的Context上下文和線程,創建出綁定不同生命周期的組件(Application,Activity,Fragment)的requestManager實例。
RequestManager職責:
- 用于管理和啟動 Glide 請求的類,通過內部的requestTracker來跟蹤記錄所有的request;
- 可以使用 Activity、Fragment 等連接生命周期事件來智能地停止、啟動和重啟請求;
通過不同的靜態with方法,獲取擁有不通生命周期的requestManager實例。
Glide#with(android.app.Activity)
Glide#with(androidx.fragment.app.FragmentActivity)
Glide#with(android.app.Fragment)
Glide#with(androidx.fragment.app.Fragment)
Glide#with(Context)
Glide#with(View)
// 對應到上述with方法,通過不同的get重載方法來創建或檢索 requestManager 對象
RequestManagerRetriever#get(android.app.Activity)
RequestManagerRetriever#get(androidx.fragment.app.FragmentActivity)
RequestManagerRetriever#get(android.app.Fragment)
RequestManagerRetriever#get(androidx.fragment.app.Fragment)
RequestManagerRetriever#get(Context)
RequestManagerRetriever#get(View)
Glide with 流程圖如下:
3.1.1、獲取 Glide 單例
首先從Glide.with(Context)方法開始
# Glide.java
// 通過retriever 的get方法來獲取requestManager
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
// 獲取 retriever 檢索器,其內部持有RequestManagerFactory,檢索器用于創建或檢索 requestManager 實例
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
return Glide.get(context).getRequestManagerRetriever();
}
// 獲取Glide,Glide內部持有檢索器
public static Glide get(@NonNull Context context) {
// 雙重檢查鎖的方式,獲取 glide 單例
if (glide == null) {
// 獲取App模塊內自定義的AppGlideModule類(*GlideModule注解的)
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
// 創建 Glide 實例
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
// checkAndInitializeGlide方法最終會調用initializeGlide方法
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder, // builder = new GlideBuilder()
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
// 獲取到創建requestManager的factory,factory會創建自動生成的GlideRequests繼承自RequestManager,其包含GlideType注解的API方法
? annotationGeneratedModule.getRequestManagerFactory()
: null;
builder.setRequestManagerFactory(factory);
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
// 構建 glide 實例,內部會創建默認的 RequestManagerRetriever和RequestManagerFactory等創建requestManager的相關參數。
Glide glide = builder.build(applicationContext);
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
// 設置到靜態變量,單例模式
Glide.glide = glide;
}
3.1.2、獲取 RequestManager 實例
在Glide創建過程中會 創建檢索器 RequestManagerRetriever,通過檢索器獲取 requestManager,接著分析 RequestManagerRetriever.get(Context)方法
# RequestManagerRetriever.java
public RequestManager get(@NonNull Context context) {
// 省略了安全檢查
if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
// 獲取 FragmentActivity 級 RequestManager
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
// 獲取 Activity 級 RequestManager
return get((Activity) context);
} else if (context instanceof ContextWrapper
// 使用 ContextWrapper 附加的 Context 繼續獲取
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
// 獲取 Application 級 RequestManager
return getApplicationManager(context);
}
// 跟蹤 FragmentActivity 方式獲取的 RequestManager
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
// 非主線程時,獲取 Application 級 RequestManager
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity); // activity 銷毀檢查
frameWaiter.registerSelf(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
// 獲取 Fragment 級 RequestManager
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
// 獲取 SupportRequestManagerFragment,其內部持有:
// 1、ActivityFragmentLifecycle,在 Fragment 的模版方法中通過 lifecycle 回調生命周期事件
// 2、RequestManagerTreeNode,用于跟蹤記錄嵌套的 Fragment 的RequestManager
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// 獲取全局 glide 單例
Glide glide = Glide.get(context);
// 通過工廠方式構建 requestManager 對象
requestManager =
factory.build(
glide,
// 1. requestManager 通過 lifecycle 注冊listener
// 2. 回調生命周期事件,在Fragment生命周期變化時,通知RequestManager實現的LifecycleListener接口方法進行響應
current.getGlideLifecycle(),
current.getRequestManagerTreeNode(), // 跟蹤嵌套Fragment內的RequestManager
context);
if (isParentVisible) {
// 開始請求,并設置target顯示
requestManager.onStart();
}
// 給 fragment 設置RequestManger
current.setRequestManager(requestManager);
}
return requestManager;
}
3.1.3、Glide 生命周期分析
RequestMananger的構造方法中,和創建的用于監聽生命周期事件的Fragment進行關聯,RequestManager實現了LifeCycleListener接口,通過LifeCycle.addListener(this)的方式將觀察者注入生命周期監視器。 RequestManager在實現了LifeCycleListener接口的onStart()/onStop()/onDestory()
的方法中,通過RequestTracker來管理請求任務,通過TargetTracker來控制View的顯示效果。
# RequestManager.java
RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory,
Context context) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.context = context;
connectivityMonitor =
factory.build(
context.getApplicationContext(),
new RequestManagerConnectivityListener(requestTracker));
// 非主線程,切換到主線程綁定生命周期事件
if (Util.isOnBackgroundThread()) {
Util.postOnUiThread(addSelfToLifecycle);
} else {
// 監聽生命周期事件
lifecycle.addListener(this);
}
// 監聽網絡變化事件
lifecycle.addListener(connectivityMonitor);
...
}
@Override
public synchronized void onStart() {
// 恢復請求
requestTracker.resumeRequests();
targetTracker.onStart();
}
@Override
public synchronized void onStop() {
// 暫停請求
requestTracker.pauseRequests();
targetTracker.onStop();
}
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
// 清理target
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
// 清除請求
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
Util.removeCallbacksOnUiThread(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
# RequestTracker.java
public void resumeRequests() {
isPaused = false;
// 處理所有的請求
for (Request request : Util.getSnapshot(requests)) {
if (!request.isComplete() && !request.isRunning()) {
// 重新發起請求
request.begin();
}
}
// pending隊列保存未完成并排隊等待再次運行的請求。 列表來維護對這些請求的硬引用,確保它們在開始運行之前或暫停時不會被垃圾收集,在重啟開啟請求時清理。
pendingRequests.clear();
}
3.1.4、Glide 網絡變化分析
- 設置自定義的網絡監聽方式;
- 未設置自定義網絡監聽方式,采用默認方式;
- 有網絡權限時,通過
ConnectivityManager.android.net.conn.CONNECTIVITY_CHANGE
廣播的方式監聽網絡事件。 - 無網絡權限時,不監聽。
# DefaultConnectivityMonitorFactory.java
// 默認監聽網絡變化廣播
public ConnectivityMonitor build(
@NonNull Context context, @NonNull ConnectivityMonitor.ConnectivityListener listener) {
int permissionResult = ContextCompat.checkSelfPermission(context, NETWORK_PERMISSION);
// 檢查網絡權限
boolean hasPermission = permissionResult == PackageManager.PERMISSION_GRANTED;
return hasPermission
? new DefaultConnectivityMonitor(context, listener)
: new NullConnectivityMonitor();
}
# ReuquestManager.java
// 網絡連接變化事件處理
private class RequestManagerConnectivityListener
implements ConnectivityMonitor.ConnectivityListener {
@GuardedBy("RequestManager.this")
private final RequestTracker requestTracker;
RequestManagerConnectivityListener(@NonNull RequestTracker requestTracker) {
this.requestTracker = requestTracker;
}
@Override
public void onConnectivityChanged(boolean isConnected) {
if (isConnected) {
synchronized (RequestManager.this) {
// 通過 requestTracker 重新發起請求
requestTracker.restartRequests();
}
}
}
}
3.2、load階段
load階段創建出 RequestBuilder 對象,為每個請求封裝 glide,requestManager,glideContext,model,requestOptions 等參數。
RequestBuilder extends BaseRequestOptions {}
3.3、into階段
into階段可以分為四個過程:
- target 綁定 request 并發起 request 請求;
- 數據加載;
- 資源解碼;
- 資源緩存和顯示;
Glide into 流程圖如下:
# RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
// 省略了安全檢查
// 省略了 options 變換
return into(
// 獲取 ImageView 載體
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
// 主線程
Executors.mainThreadExecutor());
}
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
// 構建 Request 加載請求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
// 載體原有請求與新請求比對,請求等效時采用原有請求進行加載
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
// 當請求未開始時,開始啟動請求,加載數據
previous.begin();
}
return target;
}
// 清理原來的請求
requestManager.clear(target);
// 將請求與 Target 進行綁定
target.setRequest(request);
// 記錄請求,并啟動請求
requestManager.track(target, request);
return target;
}
# RequestManager.java
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
// 記錄請求,并啟動請求
requestTracker.runRequest(request);
}
# RequestTracker.java
public void runRequest(@NonNull Request request) {
// 記錄請求
requests.add(request);
if (!isPaused) {
// 啟動請求
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
// 如果請求處于暫停狀態,則記錄加載請求,等狀態恢復時,進行重新啟動
pendingRequests.add(request);
}
}
# SingleRequest.java
public void begin() {
synchronized (requestLock) {
// 省略了安全檢查
if (status == Status.COMPLETE) {
// 直接從請求中緩存的 Resource 返回,回調給 ViewTarget 顯示資源
onResourceReady(
resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
// 從內存,本地或者遠端加載數據
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
// 顯示默認占位圖
target.onLoadStarted(getPlaceholderDrawable());
}
}
}
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
// 開啟加載,engine 是 Glide創建時構造的
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
}
}
3.3.1、數據加載流程
數據加載分為兩個部分,一部分是內存(活躍資源 HashMap 和內存 LruCache)中加載;另一部分是從本地或遠端加載。
- 緩存策略,決定緩存的數據類型:
緩存策略 | 是否支持轉換資源緩存 | 是否支持原始數據緩存 |
---|---|---|
NONE | 不支持 | 不支持 |
ALL | 數據源不是磁盤與內存緩存時,支持 | 數據源是遠程,支持 |
RESOURCE | 支持 | 不支持 |
DATA | 不支持 | 數據源不是磁盤與內存緩存時,支持 |
AUTOMIC | 數據源是本地,支持 | 數據源是遠程,支持 |
- 緩存數據類型對應的加載器:
緩存類型 | Generator | 描述 |
---|---|---|
RESOURCE | ResourceCacheGenerator | 從包含采樣/轉換資源數據的緩存文件生成DataFetcher |
DATA | DataCacheGenerator | 從包含原始未修改源數據的緩存文件生成DataFetcher |
SOURCE | SourceGenerator | 使用注冊的ModelLoaders和為加載提供的模型從原始源數據生成DataFetcher |
FINISHED | NULL | NULL |
3.3.1.1、內存數據加載流程
內存中緩存的數據分為兩種,一種是活躍資源的Map緩存,一種是LRU緩存,數據首先會從這兩個緩存中加載,如果有則直接返回使用,如果資源為null,則從本地或遠端數據加載數據。
內存數據加載流程圖如下:
# Engine.java
public <R> LoadStatus load() {
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
// 從內存中加載數據
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
// 從本地或者遠端加載數據
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
// 將內存中加載的資源回調給 ViewTarget 顯示
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
// 從獲取資源緩存中加載
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
return active;
}
// 從內存緩存中加載
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
return cached;
}
return null;
}
// 加載活躍資源
private EngineResource<?> loadFromActiveResources(Key key) {
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
// 加載內存資源
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
// 緩存活躍資源,弱引用方式保存到 Map 中
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
// 從 LruCache 中加載
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
result = (EngineResource<?>) cached;
} else {
// 使用 EngineResource包裝緩存資源
result =
new EngineResource<>(
cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
}
return result;
}
3.3.1.2、本地或遠端數據加載流程
當從內存中沒有找到資源時,會開啟本地或遠端數據加載的操作,此過程是異步行為,通過線程池方式提交加載任務啟動加載請求。
本地或者遠程數據加載流程圖如下:
# Engine.java
private <R> LoadStatus waitForExistingOrStartNewJob() {
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
// 創建 EngineJob
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
// 創建 DecodeJob,解碼流程
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob); // 開啟異步加載流程,decodeJob 實現了 Runnable 接口
return new LoadStatus(cb, engineJob);
}
# DecodeJob.java
public void run() {
// 匹配 DataFetcherGenerator 進行數據加載
runWrapped();
}
// 從 runWrapped 開始,會調用到 runGenerators 方法
private void runGenerators() {
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
// 開始加載數據,ResourceCacheGenerator,DataCacheGenerator,SourceGenerator
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
# ResourceCacheGenerator.java,源碼分析該DataFetcherGenerator
public boolean startNext() {
List<Key> sourceIds = helper.getCacheKeys();
while (modelLoaders == null || !hasNextModelLoader()) {
// Resource 是轉碼后的資源類型,對應的 Key 為 ResourceCacheKey
currentKey =
new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
// 根據 CacheFile 匹配出所有符合的 ModelLoaders
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
// 通過 ModelLoader 構造出 LoadData
loadData =
modelLoader.buildLoadData(
cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
// 通過 DataFetcher 開始加載數據
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
# FileLoader.java 內對應的 FileFetcher 嵌套類
public void loadData(@NonNull Priority priority, @NonNull DataFetcher.DataCallback<? super Data> callback) {
try {
// file 為 modelLoader.buildLoadData 時傳入的 model,即 cacheFile
// opener 是 FileInputStream
data = opener.open(file);
// 將打開的文件流數據,成功回調
callback.onDataReady(data);
} catch (FileNotFoundException e) {
// 失敗回調
callback.onLoadFailed(e);
}
}
3.3.2、資源解碼流程
在數據被加載成功之后,會進行資源的解碼操作,轉成Android可以支持顯示的資源數據。
Glide 解碼資源流程圖如下:
# DecodeJob.java
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
// 省略了變量的賦值操作
if (Thread.currentThread() != currentThread) {
// 切換到指定線程進行資源解碼操作
runReason = DecodeJob.RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
// 資源解碼
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
// 通過 LoadPath 進行解碼,loadPath 內有 decodePaths
return runLoadPath(data, dataSource, path);
}
# DecodePath.java
public Resource<Transcode> decode(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
DecodeCallback<ResourceType> callback)
throws GlideException {
// 解碼資源,DecodePath內部會匹配注冊的Decoder進行decode操作,解碼出原始的Resource
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
// 資源轉碼,DecodeJob是實現方,內部通過匹配注冊的Transformation進行transform操作
// 最后根據緩存策略,決定緩存轉碼資源或者原始資源
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
// 轉換為新資源類型,eg: Bitmap -> BitmapDrawable
return transcoder.transcode(transformed, options);
}
3.3.3、資源緩存和顯示流程
資源在經過加載和解碼之后,進行轉碼階段時,根據DataSource判斷資源是緩存原始資源還是轉碼資源,策略如下:
EncodeStrategy | 緩存到磁盤策略描述 | 緩存Key |
---|---|---|
SOURCE | 將原始資源數據緩存到磁盤 | DataCacheKey |
TRANSFORMED | 將轉碼后的資源數據緩存到磁盤 | ResourceCacheKey |
NONE | 數據不緩存到磁盤 |
資源緩存方案主要是表中三種:
緩存方案 | 方案介紹 |
---|---|
ActiveResource | 內存緩存,采用Map<Key, WeakReference> 弱引用的方案保存正在使用的資源,防止出現LRU導致正在使用的資源被回收 |
LruCache | 內存緩存,采用最近最少使用的策略,保證資源的使用效率,且盡量避免出現OOM問題 |
DiskLruCache | 磁盤緩存,最近最少使用的策略,減少對網絡耗時的請求操作 |
在經過DataFetcher.loadData數據提取之后,進行數據的一個緩存,緩存分兩種,一種是緩存到磁盤(默認是應用data目錄下的image_manager_disk_cache文件,默認大小為250M),一種是緩存到內存。
Glide 資源緩存和顯示流程圖如下:
// 1、緩存活躍資源
# Engine.java
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
if (resource != null && resource.isMemoryCacheable()) {
// 活躍資源緩存
activeResources.activate(key, resource);
}
}
# ActiveResources.java,內部通過 Map<Key, ResourceWeakReference> 緩存
synchronized void activate(Key key, EngineResource<?> resource) {
// 通過弱引用的方式持有資源
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
// 將弱引用資源放入Map
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
// 2、內存緩存資源,通過 LRU 最近最少使用方案
# Engine.java
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
// Cache 是 MemoryCache,內部持有 LruCache
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource, /*forceNextFrame=*/ false);
}
}
# LruResourceCache.java,繼承自LruCache
public synchronized Y put(@NonNull T key, @Nullable Y item) {
final int itemSize = getSize(item);
// 超出數量上限時,直接返回,并清理當前需要緩存的資源
if (itemSize >= maxSize) {
onItemEvicted(key, item);
return null;
}
if (item != null) {
currentSize += itemSize;
}
// 將資源放入 LinkedHashMap,會通過 afterNodeAccess()方法將最近訪問數據放在雙鏈表的尾部
@Nullable Entry<Y> old = cache.put(key, item == null ? null : new Entry<>(item, itemSize));
if (old != null) {
currentSize -= old.size;
// 對廢棄資源進行清理
if (!old.value.equals(item)) {
onItemEvicted(key, old.value);
}
}
// 重新計算緩存空間,大小超出時,則移除最久未使用的資源
evict();
return old != null ? old.value : null;
}
// 3、磁盤緩存資源,采用 DiskLruCache 方案
# DecodeJob.java 的嵌套類 DeferredEncodeManager
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
// 寫入資源時,根據緩存策略,已經確定 toEncode 資源是轉碼資源還是原始資源
diskCacheProvider
.getDiskCache()
.put(key, new DataCacheWriter<>(encoder, toEncode, options));
} finally {
// 資源回收
toEncode.unlock();
GlideTrace.endSection();
}
}
// DiskLruCacheWrapper.java 內部持有 DiskLruCache
private synchronized DiskLruCache getDiskCache() throws IOException {
if (diskLruCache == null) {
// 打開磁盤緩存器
diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize);
}
return diskLruCache;
}
public void put(Key key, Writer writer) {
// 將 Key 安全的轉成 sha256 字符串編碼,key會被保存在LruCache內,獲取時會加鎖
String safeKey = safeKeyGenerator.getSafeKey(key);
// 加鎖,進行安全的寫入操作
writeLocker.acquire(safeKey);
try {
try {
// 獲取 DiskLruCache
DiskLruCache diskCache = getDiskCache();
// 如果資源已緩存,則退出
Value current = diskCache.get(safeKey);
if (current != null) {
return;
}
// 獲取資源寫入的編輯器
DiskLruCache.Editor editor = diskCache.edit(safeKey);
if (editor == null) {
throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
}
try {
// 寫入到文件內
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Unable to put to disk cache", e);
}
}
} finally {
// 釋放鎖
writeLocker.release(safeKey);
}
}
# Extectors.java
// 主線程池,用于顯示圖片
private static final Executor MAIN_THREAD_EXECUTOR =
new Executor() {
@Override
public void execute(@NonNull Runnable command) {
Util.postOnUiThread(command);
}
}
4、總結
使用建議:
1. 結合自身需求,決定是否考慮在AppGlideModule內應用全局性的Options。
2. 在使用Glide時,盡可能的保證context上下文符合預期,防止產生內存泄漏問題。
3. 在滑動事件時,可以考慮結合RequestManager內的 resume 和 pause 來處理快速滑動產生的卡頓問題。
總結,Glide 框架主要分為三個部分:
- 第一個部分: with 階段,注冊編解碼器,初始化變量(Glide,RequestManager,Engine等)和綁定頁面生命周期等操作,用于管理請求和監聽生命周期事件。
- 第二個部分:load 階段,為每個請求配置單獨的 Option,比如:設置 width,height,DiskCacheStrategy,Transaction等。
- 第三個部分:into 階段,最復雜的階段,啟動請求,開始加載數據,對數據進行解碼和轉碼操作,緩存解碼數據或者原始數據,顯示視圖。
作者:jaymzyang
轉載于:https://juejin.cn/post/7044079102839488543
如有侵權,請聯系刪除!