从Glide图片加载流程深入分析源码
不知道有没有小伙伴跟我一样,使用Glide已经有好多年,平时看源码比较零碎,对Glide的源码只能算一知半解,面试遇到分析Glide的源码,无从说起……
那今天就从0开始,对Glide源码进行一个整体的梳理,更进一步的了解Glide的原理。
注意:此文章基于Glide 4.11.0版本
众所周知,我们使用Gilde加载图片时,最常用的一行代码:
Glide.with(context).load(url).into(imageView)
这里就只先分析此行代码的主线流程,省略了其他的配置方法。
1. Glide.with()
首先从Glide的with
方法开始,它是我们最开始调用的方法,有多个重载:
无论是哪个重载方法,内部都是return了一行 getRetriever().get()
代码:
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
先调用 getRetriever
返回了一个 RequestManagerRetriever
对象,然后在调用 RequestManagerRetriever
的 get
方法返回一个RequestManager
对象
RequestManagerRetriever
的get
方法,也是有多个重载:
其中最大的区别就是在于context和非context的参数的方法,实现各不相同:
如果是非Context的参数,比如 activity、fragment、view
,其中fragment
和view
都可以找到一个所属的Activity对象,那么最后就会调用到get(Activity activity)
,并内部调用fragmentGet
方法:
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else if (activity instanceof FragmentActivity) {
return get((FragmentActivity) activity);
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
在fragmentGet
方法中,创建了一个Fragment和RequestManager:
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// ...
}
return requestManager;
}
这里稍微提一下RequestManagerFragment
,getRequestManagerFragment
创建的RequestManagerFragment是Glide用来进行图片加载的生命周期管理的,最终通过FragmentManager添加到Activity上,它本身什么都不展示,只是一个空的Fragment,仅仅是通过Fragment的生命周期来管理图片加载的请求,避免内存泄漏问题,可以看getRequestManagerFragment
方法的实现:
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
回到主线,我们到这里就从RequestManagerRetriever
中获得了一个RequestManager
对象,从而就可以开始第二步动作:load
2. Glide.with().load()
当我们获得一个RequestManager
后,就可以接着调用.load()
的方法,现在的调用就就到了Glide.with().load()
,可以看到load也是有多个重载方法,可以支持各种数据的load,包括file、string的url,uri等等……
load内部是通过调用asDrawable
方法,asDrawable
再调用了as
方法,创建了一个RequestBuilder
并返回:
asDrawable()
:
public RequestBuilder asDrawable() {
return as(Drawable.class);
}
as()
:
public RequestBuilder as(
@NonNull Class resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
Glide.with().load().into()
这就是我们调用load方法之后得到RequestBuilder
的由来,得到RequestBuilder
之后,按照逻辑顺序调用RequestBuilder
的into
方法,也是主线逻辑里面最重要的部分:
private > Y into(
@NonNull Y target,
@Nullable RequestListener targetListener,
BaseRequestOptions> options,
Executor callbackExecutor) {
// ...
// 构建Request请求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
// ...
return target;
}
requestManager.clear(target);
target.setRequest(request);
// 执行加载请求
requestManager.track(target, request);
return target;
}
这里省略了不相关的代码,into
方法中,主要是构建了一个Request
对象,然后调用requestManager.track(target, request)
开始加载图片,继续跟踪到requestManager.track
方法,位于RequestManager.java
中:
synchronized void track(@NonNull Target> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
可以看到request继续传递到requestTracker.runRequest(request)
中,可以继续跟踪看看:
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
// ...
}
}
在RequestTracker.java
类里面,runRequest
调用了request.begain()
方法
由于Request
是一个接口,这里的request
对象在构建的时候其实是构建了它的实现类SingleRequest
,位于RequestBuilder.java
:
private Request obtainRequest(
Object requestLock,
Target target,
RequestListener targetListener,
BaseRequestOptions> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
return SingleRequest.obtain(
context,
glideContext,
requestLock,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
那么在SingleRequest
的begin
方法里面,判断语句有好几个,千万要迷路,这里的重点是onSizeReady
:
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
// ...
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
// ...
}
}
继续跟踪onSizeReady
,里面使用engine
对象调用了load
方法:
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
// ...
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);
// ...
}
}
继续分析Engine的load方法,这里加载请求就揭开了Gilde的缓存机制:
public LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class> resourceClass,
Class transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map, 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;
// 构建加载资源的key
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);
}
}
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
首先Glide是 loadFromMemory
从内存中加载资源,这里的内存细分一下,分别是 活动资源
和 内存缓存
,Glide是优先从活动资源中获取资源,获取不到再从内存缓存中获取,如果内存缓存也获取不到,loadFromMemory
就返回null:
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);
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
// 从内存缓存中读取读取到了资源
return cached;
}
return null;
}
这里从内存缓存中读取到了资源之后,还把资源加入到活动缓存,这样做的目的是为了下一次获取数据更快(先从活动资源获取数据):
private EngineResource> loadFromCache(Key key) {
EngineResource> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
现在回过头来看 waitForExistingOrStartNewJob
方法,这个方法是从 loadFromMemory
没有加载到数据时才调用,waitForExistingOrStartNewJob
里面构建了 engineJob
和 decodeJob
的对象,engineJob
对象用来启动了一个 decodeJob
,engineJob
很简单,就是维护了线程池,进行线程的调度,这里在start
里面执行了 decodeJob
:
public synchronized void start(DecodeJob decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
由此看来,既然是线程调度,decodeJob
是被执行的部分,那DecodeJob
类肯定是实现了 Runnable 接口,并将主要的逻辑写到了 run 方法里面,跟踪代码一看,果不其然:
class DecodeJob
implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable>,
Poolable {
// ...
}
避免干扰,我将run
方法里面的注释和不相关的逻辑给删除掉了:
public void run() {
// ...
try {
// ...
// 主要代码,我们继续跟踪这个方法
runWrapped();
} catch (CallbackException e) {
throw e;
} catch (Throwable t) {
// ...
} finally {
// ...
}
}
继续跟踪runWrapped
方法:
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
// 首次初始化并获取资源
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
// 从磁盘缓存获取不到数据,重新获取
runGenerators();
break;
case DECODE_DATA:
// 获取资源成功,解码数据
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
这里通过我的注释应该很容易理解这个switch语句了,至于获取图片成功后的 decodeFromRetrievedData
,暂时停一下稍候分析,其他的两个case语句,不管是 INITIALIZE
初始化还是 SWITCH_TO_SOURCE_SERVICE
从磁盘缓存获取不到数据进行重试,都是要调用 runGenerators
来获取数据,我们继续跟踪看看这个方法:
private void runGenerators() {
//...
// 条件语句中的 currentGenerator.startNext() 才是重点
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// ...
}
首先看while的条件语句:
- !isCancelled :如果没有取消请求
- currentGenerator != null : currentGenerator对象不为空
- !(isStarted = currentGenerator.startNext()) : currentGenerator.startNext()的执行结果会赋值给 isStarted
前两个不过多解释,主要是第三个语句:如果isStarted的值为false,表示没有执行成功,就会执行while语句体里面的内容,while语句体里面就会获取下一个 Stage
,以及通过Stage
来获取对应的 Generator
:
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);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
根据两个方法联合分析,getNextGenerator
执行结果:
- 第一次返回
ResourceCacheGenerator
- 第二次返回
DataCacheGenerator
- 第三次的返回取决于
getNextStage
方法中case DATA_CACHE
返回Stage.FINISHED
或Stage.SOURCE
,getNextGenerator
方法就返回SourceGenerator
或者是 null
那其实这三个Generator:ResourceCacheGenerator
、DataCacheGenerator
、SourceGenerator
,都是实现自DataFetcherGenerator
接口,目的是为了从不同的Generator中加载资源,如果加载成功,就将资源返回,如果加载不成功,就继续找下一个Generator加载
不管是从哪个Generator加载数据,都是通过刚才的while语句中的startNext
来执行,不同的Generator内部实现不一样,但是都大同小异,都是加载数据,加载结果是通过一个叫做FetcherReadyCallback的接口进行回调,这里随便看一个,DataCacheGenerator
的startNext
:
public boolean startNext() {
// ...
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(
cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
// 加载资源重点代码
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
加载资源的重点代码是:loadData.fetcher.loadData(helper.getPriority(), this)
loadData.fetcher
获得的是一个DataFetcher
,DataFetcher
是获取资源的接口,所以loadData
具体的工作其实是交给它的实现类来完成,它的实现类有HttpUrlFetcher
、LocalUriFetcher
、FileFetcher
、AssetPathFetcher
等等,用于支持各种资源的获取。
至于加载的细节,这里举两个例子:
- 从http url加载
public void loadData(
@NonNull Priority priority, @NonNull DataCallback super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}
- 从文件加载
public void loadData(@NonNull Priority priority, @NonNull DataCallback super Data> callback) {
try {
data = opener.open(file);
callback.onDataReady(data);
} catch (FileNotFoundException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to open file", e);
}
callback.onLoadFailed(e);
}
}
加载成功,通过callback.onDataReady(data)
进行回调,加载不成功,则通过callback.onLoadFailed(e)
进行回调。
以上就是Glide加载图片的主要流程代码分析,因为是省略了大部分的源码和注释,只保留了主线代码,所以还是要自己去跟踪一遍,才更能理解其中的原理,谢谢观看!