Glide源碼剖析系列
- Android Glide源碼剖析系列(一)圖片加載請求如何感知組件生命周期
- Android Glide源碼剖析系列(二)Glide如何管理圖片加載請求
- Android Glide源碼剖析系列(三)深入理解Glide圖片加載流程
- Android Glide源碼剖析系列(四)緩存機制及其原理
為什么選擇Glide嗓蘑?
- 多種圖片格式的緩存,適用于更多的內(nèi)容表現(xiàn)形式(如Gif、WebP桩皿、縮略圖豌汇、Video)
- 生命周期集成(根據(jù)Activity或者Fragment的生命周期管理圖片加載請求)Glide可以感知調(diào)用頁面的生命周期,這就是優(yōu)勢
- 高效處理Bitmap(bitmap的復(fù)用和主動回收泄隔,減少系統(tǒng)回收壓力)
- 高效的緩存策略拒贱,靈活(Picasso只會緩存原始尺寸的圖片,Glide緩存的是多種規(guī)格)佛嬉,加載速度快且內(nèi)存開銷械羰摺(默認Bitmap格式的不同麻敌,使得內(nèi)存開銷是Picasso的一半)
小結(jié):支持圖片格式多;Bitmap復(fù)用和主動回收;生命周期感應(yīng)荐操;優(yōu)秀的緩存策略慕趴;加載速度快(Bitmap默認格式RGB565)
Glide簡單使用
Glide.with(this)
.load("https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF")
.error(R.drawable.aaa)
.placeholder(R.drawable.ic_android_black_24dp)
.fallback(R.drawable.aaa)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(imageView);
源碼分析
上篇文章學(xué)習了RequestTracker 如何管理圖片加載請求眨业,本文開始分析圖片加載請求執(zhí)行流程推盛。
#RequestTracker.java
/** Starts tracking the given request. 把請求添加到requests和pendingRequests*/
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin(); //圖片加載請求執(zhí)行入口
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
request.begin()
方法是請求開始執(zhí)行的入口,這里的request是SingleRequest類型钝腺,所以有必要先搞懂SingleRequest類
/**
* 定義:把資源加載到目標的請求
*/
public final class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {
//請求的狀態(tài)
private enum Status {
/** Created but not yet running. */
PENDING,
/** In the process of fetching media. */
RUNNING,
/** Waiting for a callback given to the Target to be called to determine target dimensions. */
WAITING_FOR_SIZE,
/** Finished loading media successfully. */
COMPLETE,
/** Failed to load media, may be restarted. */
FAILED,
/** Cleared by the user with a placeholder set, may be restarted. */
CLEARED,
}
/* 同步鎖對象:給請求的操作方法上鎖抛姑,保證線程安全 */
private final Object requestLock;
//監(jiān)聽請求的狀態(tài)變化
@Nullable private final RequestListener<R> targetListener;
//協(xié)調(diào)一個target上的多個請求
private final RequestCoordinator requestCoordinator;
private final Context context;
private final GlideContext glideContext;
@Nullable private final Object model;
private final Class<R> transcodeClass;
//請求的配置參數(shù)
private final BaseRequestOptions<?> requestOptions;
private final int overrideWidth;
private final int overrideHeight;
private final Priority priority;
private final Target<R> target;
@Nullable private final List<RequestListener<R>> requestListeners;
private final TransitionFactory<? super R> animationFactory;
private final Executor callbackExecutor;
@GuardedBy("requestLock")
private Resource<R> resource;
@GuardedBy("requestLock")
private Engine.LoadStatus loadStatus;
@GuardedBy("requestLock")
private long startTime;
// Volatile because it's accessed outside of a lock and nullable, even though in practice it will
// always be non-null unless the request is in the object pool.
private volatile Engine engine; //圖片處理引擎
/* Variables mutated during a request. */
@GuardedBy("requestLock")
private Status status;
@GuardedBy("requestLock")
@Nullable
private Drawable errorDrawable; //加載失敗顯示圖
@GuardedBy("requestLock")
@Nullable
private Drawable placeholderDrawable; //占位圖
@GuardedBy("requestLock")
@Nullable
private Drawable fallbackDrawable;
@GuardedBy("requestLock")
private int width;
@GuardedBy("requestLock")
private int height;
@Override
public void begin() {
synchronized (requestLock) { //加鎖
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) { //注釋1
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel); //加載失敗
return;
}
if (status == Status.RUNNING) { //注釋2
throw new IllegalArgumentException("Cannot restart a running request"); //直接拋出異常
}
// If we're restarted after we're complete (usually via something like a notifyDataSetChanged
// that starts an identical request into the same Target or View), we can simply use the
// resource and size we retrieved the last time around and skip obtaining a new size, starting
// a new load etc. This does mean that users who want to restart a load because they expect
// that the view size has changed will need to explicitly clear the View or Target before
// starting the new load.
if (status == Status.COMPLETE) { //注釋3
onResourceReady(
resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
experimentalNotifyRequestStarted(model);
cookie = GlideTrace.beginSectionAsync(TAG);
status = Status.WAITING_FOR_SIZE; //狀態(tài)設(shè)為:等待target確定尺寸
if (Util.isValidDimensions(overrideWidth, overrideHeight)) { //注釋4
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) { //target設(shè)置占位圖
target.onLoadStarted(getPlaceholderDrawable());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
}
}
逐個分析Request#begin()方法中的if判斷語句
-
注釋1:如果資源數(shù)據(jù)為null,執(zhí)行
onLoadFailed(new GlideException("Received null model"), logLevel)
方法艳狐;
private void onLoadFailed(GlideException e, int maxLogLevel) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) { //加鎖
loadStatus = null;
status = Status.FAILED; //1.設(shè)置請求的狀態(tài)
isCallingCallbacks = true;
try {
// TODO: what if this is a thumbnail request?
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onLoadFailed(e, model, target, isFirstReadyResource());
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onLoadFailed(e, model, target, isFirstReadyResource());
if (!anyListenerHandledUpdatingTarget) {
setErrorPlaceholder(); //2.如果不是縮略圖請求定硝,設(shè)置顯示失敗占位圖
}
} finally {
isCallingCallbacks = false;
}
notifyLoadFailed();
}
}
@GuardedBy("requestLock")
private void setErrorPlaceholder() {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = null;
if (model == null) {
error = getFallbackDrawable();
}
// Either the model isn't null, or there was no fallback drawable set.
if (error == null) {
error = getErrorDrawable();
}
// The model isn't null, no fallback drawable was set or no error drawable was set.
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(error); //3.告訴target資源加載失敗,并把錯誤占位圖資源回傳給target
}
- 設(shè)置請求的狀態(tài)為Status.FAILED毫目;
- 如果不是縮略圖請求蔬啡,setErrorPlaceholder()設(shè)置顯示失敗占位圖;
- 告訴target資源加載失敗镀虐,并把錯誤占位圖資源回傳給target
-
注釋2:請求狀態(tài)為RUNNING箱蟆,不允許執(zhí)行
begin()
方法 -
注釋3:請求狀態(tài)為COMPLETE,執(zhí)行
onResourceReady( resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
@Override
public void onResourceReady(
Resource<?> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
stateVerifier.throwIfRecycled();
Resource<?> toRelease = null;
try {
synchronized (requestLock) {
loadStatus = null;
if (resource == null) { //資源為null刮便,回調(diào)onLoadFailed
GlideException exception =
new GlideException(
"Expected to receive a Resource<R> with an "
+ "object of "
+ transcodeClass
+ " inside, but instead got null.");
onLoadFailed(exception);
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
toRelease = resource;
this.resource = null;
GlideException exception =
new GlideException(
"Expected to receive an object of "
+ transcodeClass
+ " but instead"
+ " got "
+ (received != null ? received.getClass() : "")
+ "{"
+ received
+ "} inside"
+ " "
+ "Resource{"
+ resource
+ "}."
+ (received != null
? ""
: " "
+ "To indicate failure return a null Resource "
+ "object, rather than a Resource object containing null data."));
onLoadFailed(exception);
return;
}
if (!canSetResource()) {
toRelease = resource; //準備回收resource
this.resource = null;
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
GlideTrace.endSectionAsync(TAG, cookie);
return;
}
onResourceReady(
(Resource<R>) resource, (R) received, dataSource, isLoadedFromAlternateCacheKey);
}
} finally {
if (toRelease != null) {
engine.release(toRelease);
}
}
}
@GuardedBy("requestLock")
private boolean canSetResource() {
return requestCoordinator == null || requestCoordinator.canSetImage(this);
}
- 如果 resource 或 resource.get() 為空空猜,調(diào)用 onLoadFailed(exception);
- 如果不允許設(shè)置resource,請求狀態(tài)設(shè)為COMPLETE恨旱,并準備回收resource
- 執(zhí)行
onResourceReady( (Resource<R>) resource, (R) received, dataSource, isLoadedFromAlternateCacheKey);
@GuardedBy("requestLock")
private void onResourceReady(
Resource<R> resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
GlideTrace.endSectionAsync(TAG, cookie);
}
資源加載成功回調(diào)target.onResourceReady()方法
-
注釋4:如果通過RequestOption設(shè)置的寬高都大于0或者與Target原始寬高相等辈毯,則調(diào)用
onSizeReady(overrideWidth, overrideHeight);
;否則重新計算target尺寸搜贤,計算完成后依然會調(diào)用onSizeReady()方法
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING; //請求的狀態(tài)設(shè)為RUNNING
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
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);
// This is a hack that's only useful for testing right now where loads complete synchronously
// even though under any executor running on any thread but the main thread, the load would
// have completed asynchronously.
if (status != Status.RUNNING) {
loadStatus = null;
}
}
}
- 請求的狀態(tài)設(shè)為RUNNING
- 圖片加載引擎Engine開始加載圖片
到此為止谆沃,Request的begin()方法分析完畢,圖片加載流程最終交給Engine執(zhí)行仪芒。Engine是整個圖片加載流程中一個非常重要的角色唁影,接下來繼續(xù)閱讀源碼揭開Engine的神秘面紗耕陷。
#Engine.java
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
//使用request配置信息構(gòu)建EngineKey,用于資源緩存
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); //從內(nèi)存緩存中查找資源
if (memoryResource == null) { //緩存中查找不到資源据沈,重用或新建一個新EngineJob
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
// Avoid calling back while holding the engine lock, doing so makes it easier for callers to
// deadlock.
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
@Nullable
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = loadFromActiveResources(key); //正在使用的資源列表中查找
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return active;
}
EngineResource<?> cached = loadFromCache(key); //內(nèi)存緩存中查找資源
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return cached;
}
return null;
}
- 使用request配置信息構(gòu)建EngineKey啃炸,用作資源緩存時的key
- 從正在使用的資源列表和內(nèi)存緩存中查找資源
- 如果沒找到資源,重用或新建一個新EngineJob
注:Glide著名的圖片緩存分為內(nèi)存緩存和磁盤緩存卓舵,這里已經(jīng)出現(xiàn)了內(nèi)存緩存,磁盤緩存會在接下來的DecodeJob中處理膀钠。關(guān)于圖片緩存機制會專門擼一篇文章掏湾,所以本文中會略過緩存處理相關(guān)邏輯。
private <R> LoadStatus waitForExistingOrStartNewJob(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor,
EngineKey key,
long startTime) {
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
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);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
從EngineJob緩存列表中查找是否有可重用的EngineJob肿嘲,如果有直接重用融击;否則新建一個EngineJob,并開啟該JobengineJob.start(decodeJob);
#EngineJob.java
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
解碼工作是耗時操作雳窟,不能在主線程操作尊浪,因此把decodeJob提交到線程池執(zhí)行
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback, Runnable,
Comparable<DecodeJob<?>>, Poolable {
@Override
public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (CallbackException e) {
throw e;
} catch (Throwable t) {
// 異常處理
} finally {
//清理工作
}
}
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE); //1
currentGenerator = getNextGenerator(); //2
runGenerators(); //3
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE
: getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this); //緩存的處理過的資源的生成器
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this); //緩存的原始資源的生成器
case SOURCE:
return new SourceGenerator(decodeHelper, this); //使用modelLoader和model獲取原始資源的生成器
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
- 計算nextStage
- 根據(jù)nextStage獲取對應(yīng)的Generator
- 執(zhí)行runGenerators()
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}
while循環(huán)語句:圖片可能來自于磁盤緩存,也可能需要開啟任務(wù)去獲取封救。我們分析的是沒有緩存的情況拇涤,所以要先確定使用哪種Generator來生成資源,然后繼續(xù)查看加載流程誉结,此處以SourceGenerator為例:
繼續(xù)分析sourceGenerator.startNext()
:
#SourceGenerator.java
@Override
public boolean startNext() {
//跳過緩存相關(guān)邏輯鹅士,分析沒有緩存時的加載流程
while (!started && hasNextModelLoader()) { //注釋5
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
startNextLoad(loadData);
}
}
return started;
}
我們知道資源加載由ModelLoader才能完成,先分析一下ModelLoader類
public interface ModelLoader<Model, Data> {
class LoadData<Data> {
public final Key sourceKey; //用于緩存的key
public final List<Key> alternateKeys;
public final DataFetcher<Data> fetcher; //數(shù)據(jù)抓取器
public LoadData(@NonNull Key sourceKey, @NonNull DataFetcher<Data> fetcher) {
this(sourceKey, Collections.<Key>emptyList(), fetcher);
}
public LoadData(
@NonNull Key sourceKey,
@NonNull List<Key> alternateKeys,
@NonNull DataFetcher<Data> fetcher) {
this.sourceKey = Preconditions.checkNotNull(sourceKey);
this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
this.fetcher = Preconditions.checkNotNull(fetcher);
}
}
@Nullable
LoadData<Data> buildLoadData(
@NonNull Model model, int width, int height, @NonNull Options options);
boolean handles(@NonNull Model model);
}
原來真正干活的是內(nèi)部類LoadData惩坑,其中的sourceKey用于緩存資源掉盅,fetcher是數(shù)據(jù)抓取器。
回到sourceGenerator.startNext()
注釋5循環(huán)語句:
#DecodeHelper.java
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
根據(jù)model類型從ModelLoader注冊列表中篩選出符合要求的ModelLoader列表以舒,最終篩選出能干活的LoadData去執(zhí)行startNextLoad(loadData)
#SourceGenerator.java
private void startNextLoad(final LoadData<?> toStart) {
//注釋6:加載資源
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback<Object>() {
@Override
public void onDataReady(@Nullable Object data) {
if (isCurrentRequest(toStart)) {
onDataReadyInternal(toStart, data);
}
}
@Override
public void onLoadFailed(@NonNull Exception e) {
if (isCurrentRequest(toStart)) {
onLoadFailedInternal(toStart, e);
}
}
});
}
注釋6:最終加載工作交由Fetcher處理趾痘,由于我們傳入的model是String類型的圖片地址,所以這里干活的是HttpUrlFetcher蔓钟,進入HttpUrlFetcher#loadData():
#HttpUrlFetcher.java
@Override
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
callback.onLoadFailed(e);
} finally {
}
}
使用loadDataWithRedirects()方法獲取網(wǎng)絡(luò)資源的InputStream永票,并且把結(jié)果回調(diào)給callback.onDataReady(result)方法,代碼執(zhí)行到注釋6處的onDataReady()回調(diào)方法奋刽,內(nèi)部調(diào)用onDataReadyInternal(toStart, data)
繼續(xù)處理inputStream
#SourceGenerator.java
@Synthetic
void onDataReadyInternal(LoadData<?> loadData, Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
cb.reschedule();
} else {
cb.onDataFetcherReady(
loadData.sourceKey,
data,
loadData.fetcher,
loadData.fetcher.getDataSource(),
originalKey);
}
}
由于我們是禁用緩存的瓦侮,直接進入到else語句,這里又是一個方法回調(diào)佣谐,這個回調(diào)方法在DecodeJob中實現(xiàn)
#DecodeJob.java
@Override
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
this.isLoadingFromAlternateCacheKey = sourceKey != decodeHelper.getCacheKeys().get(0);
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
這里需要經(jīng)歷一系列的方法調(diào)用來處理數(shù)據(jù):decodeFromRetrievedData() -> decodeFromData(currentFetcher, currentData, currentDataSource) -> decodeFromFetcher(data, dataSource)
#DecodeJob.java
@SuppressWarnings("unchecked")
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass()); //根據(jù)data類型獲取LoadPath
return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath(
Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)
throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
#LoadPath.java
public Resource<Transcode> load(
DataRewinder<Data> rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback)
throws GlideException {
List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
}
private Resource<Transcode> loadWithExceptionList(
DataRewinder<Data> rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions)
throws GlideException {
Resource<Transcode> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
#DecodePath.java
public Resource<Transcode> decode(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
DecodeCallback<ResourceType> callback)
throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
目標資源經(jīng)過一系列解碼(decode)和轉(zhuǎn)換(transform)操作肚吏,最終得到我們需要的BitmapDrawableResource并一層層返回給調(diào)用發(fā)起的地方,也就是decodeFromRetrievedData()方法狭魂,我們回到該方法:
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey(
"Retrieved data",
startFetchTime,
"data: "
+ currentData
+ ", cache key: "
+ currentSourceKey
+ ", fetcher: "
+ currentFetcher);
}
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource, isLoadingFromAlternateCacheKey);
} else {
runGenerators();
}
}
把decode得到的結(jié)果resource傳入notifyEncodeAndRelease()方法
private void notifyEncodeAndRelease(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
GlideTrace.beginSection("DecodeJob.notifyEncodeAndRelease");
try {
if (resource instanceof Initializable) {
((Initializable) resource).initialize(); //1
}
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
notifyComplete(result, dataSource, isLoadedFromAlternateCacheKey); //2
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode
// process
// throws.
onEncodeComplete();
} finally {
GlideTrace.endSection();
}
}
- 執(zhí)行繪制Bitmap的準備工作
@Override
public void initialize() {
drawable.getBitmap().prepareToDraw();
}
- 圖片資源decode完畢罚攀,可以拿去顯示圖片啦
跨越千山萬水馬上到達終點了党觅,懷著激動的心情繼續(xù)分析代碼:
private void notifyComplete(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource, isLoadedFromAlternateCacheKey);
}
onResourceReady()方法在EngineJob中實現(xiàn)。繼續(xù)追蹤代碼:
#EngineJob.java
@Override
public void onResourceReady(
Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
this.isLoadedFromAlternateCacheKey = isLoadedFromAlternateCacheKey;
}
notifyCallbacksOfResult();
}
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
//我們省略了異常情況的處理代碼和一些注釋代碼
engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener); //1
hasResource = true;
copy = cbs.copy(); //2
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
engineJobListener.onEngineJobComplete(this, localKey, localResource); //3
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb)); //4
}
decrementPendingCallbacks();
}
- engineResource 是資源的一個包裝類斋泄,負責計算資源被引用的次數(shù)杯瞻,次數(shù)為0的時候可以回收資源
- copy內(nèi)部包裝的是
Executors.mainThreadExecutor()
主線程池,方便切換到主線程 - EngineJob執(zhí)行完畢炫掐,把加載的資源加入內(nèi)存緩存并且從EngineJob緩存列表移除當前job
@Override
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null && resource.isMemoryCacheable()) {
activeResources.activate(key, resource);
}
jobs.removeIfCurrent(key, engineJob);
}
- 把任務(wù)切換到主線程執(zhí)行魁莉,也就是說之前的操作都是在子線程中處理
@Override
public void run() {
// Make sure we always acquire the request lock, then the EngineJob lock to avoid deadlock
// (b/136032534).
synchronized (cb.getLock()) {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
// Acquire for this particular callback.
engineResource.acquire(); //增加資源引用次數(shù)
callCallbackOnResourceReady(cb); //繼續(xù)回調(diào)
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
@GuardedBy("this")
void callCallbackOnResourceReady(ResourceCallback cb) {
try {
// This is overly broad, some Glide code is actually called here, but it's much
// simpler to encapsulate here than to do so at the actual call point in the
// Request implementation.
cb.onResourceReady(engineResource, dataSource, isLoadedFromAlternateCacheKey);
} catch (Throwable t) {
throw new CallbackException(t);
}
}
最終還是回到了SingleRequest的onResourceReady(),也就是文章開頭介紹的begin()方法注釋3部分募胃,太遠了重新貼一下代碼吧旗唁!
@GuardedBy("requestLock")
private void onResourceReady(
Resource<R> resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
//1 begin
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
//1 end
if (!anyListenerHandledUpdatingTarget) { //2
Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
GlideTrace.endSectionAsync(TAG, cookie);
}
- 如果設(shè)置了requestListener或targetListener,則調(diào)用它們的onResourceReady()回調(diào)方法痹束;
- anyListenerHandledUpdatingTarget含義:是否有l(wèi)istener處理检疫,即listener.onResourceReady()方法的返回值。
如果anyListenerHandledUpdatingTarget==false祷嘶,進入ImageViewTarget#onResourceReady()方法:
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource); //抽象方法屎媳,子類中實現(xiàn)
maybeUpdateAnimatable(resource);
}
進入DrawableImageViewTarget#setResource(resource)方法:
@Override
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
最終資源被成功設(shè)置到ImageView上,圖片加載流程結(jié)束论巍。
如果有錯誤或理解不到位的地方烛谊,歡迎批評指正。
Tips:整個加載流程的代碼調(diào)用真的是很復(fù)雜环壤,涉及到Callback回調(diào)晒来、抽象方法實現(xiàn)、還有參數(shù)N次傳遞后很難定位來源郑现,建議一邊調(diào)試程序一邊跟蹤代碼湃崩,可以直接跳轉(zhuǎn)到執(zhí)行點。