目录
[2.1 构建一个Request对象](#2.1 构建一个Request对象)
[2.2 缓存检测](#2.2 缓存检测)
[2.3 如果缓存中没有,构建新的异步任务](#2.3 如果缓存中没有,构建新的异步任务)
[2.4 解压inputstream 采样压缩 得到bitmap 转换为Drawable](#2.4 解压inputstream 采样压缩 得到bitmap 转换为Drawable)
[2.5 构建磁盘缓存 内存缓存](#2.5 构建磁盘缓存 内存缓存)
[2.6 显示图片](#2.6 显示图片)
关于glide的使用及with部分源码分析可以参考文章Android-Glide详解-CSDN博客
一,load源码分析
下面我们先来看下load的具体代码:
Kotlin
val requestBulider:RequestBuilder<Drawable> =requestManager.load(url)
java
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
@SuppressWarnings("deprecation")
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return asDrawable().load(resourceId);
}
@SuppressWarnings("deprecation")
@CheckResult
@Override
@Deprecated
public RequestBuilder<Drawable> load(@Nullable URL url) {
return asDrawable().load(url);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable byte[] model) {
return asDrawable().load(model);
}
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
从上面可以看出,load可以加载多种类型的对象。
先看下asDrawable():
java
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
继续往下看as:
java
@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
这里可以看出 实例化了一个RequestBuilder。然后返回来继续看asDrawable().load(string)方法:
java
@NonNull
@Override
@CheckResult
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
然后看看loadGenericv方法:
java
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
//加载的数据源
this.model = model;
//这个请求是否已经添加了加载的数据源
isModelSet = true;
return this;
}
到此为止,RequestBuilder就构建完成了,load的使命也完成了。
二,into源码分析
into是Glide里面最为复杂的部分,要想详细的分析完整,一时半会是不可能的,在这里我只能大致的分析下into的流程,不一点一点的分析了,因为它是过程很复杂,但是比较好理解,不像rxjava那样绕来绕去,所以一点一点的分析没有意义。
2.1 构建一个Request对象
因为load已经构建了一个RequestBuilder对象,所以在它的into方法里我们看到:
java
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
。。。
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
。。。
return target;
}
通过buildRequest构建了一个Request对象,具体看看这个方法都构造了什么参数:
java
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
return buildRequestRecursive(
/*requestLock=*/ new Object(),
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
有请求的宽,高,采样等等
继续往下看,发现最终走到了:
java
private Request obtainRequest(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> 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对象。
2.2 缓存检测
在SingleRequest中,它会调用Engine的load方法:
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;
//拿到缓存或者请求的 key
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
//根据 key 拿到活动缓存或者内存缓存中的资源
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);
}
}
//如果 ActiveResources 活动缓存中有就回调出去
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
java
@Nullable
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
//根据key从活动缓存中拿资源
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return active;
}
//活动缓存没有 就从LruResourceCache 中找寻这个资源
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return cached;
}
return null;
}
java
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) {
//根据 Key 看看缓存中是否正在执行
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCach
if (current != null) {
//如果正在执行,把数据回调出去
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime
}
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);
//把当前需要执行的 key 添加进缓存
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);
}
通过 engine.load 这个函数里面的逻辑,可以总结3点:
-
先构建请求或者缓存 KEY ;
-
根据 KEY 从内存缓存中查找对应的资源数据(ActiveResources(活动缓存,内部 是一个 Map 用弱引用持有),LruResourceCache),如果有就回调 对应监听的 onResourceReady 表示数据准备好了。
-
从执行缓存中查找对应 key 的任务
-
如果找到了,就说明已经正在执行了,不用重复执行。
-
没有找到,通过 EngineJob.start 开启一个新的请求任务执行。
2.3 如果缓存中没有,构建新的异步任务
通过上面代码可以看出,异步任务是通过engineJob.start方法开始执行的:
java
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
它里面是通过一个线程池来执行的,具体的runnable任务就是decodeJob的run方法:
java
@Override
public void run() {
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (CallbackException e) {
throw e;
} catch (Throwable t) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(
TAG,
"DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage,
t);
}
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
throw t;
} finally {
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
然后接着看看runWrapped()方法:
java
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);
}
}
java
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
//如果外部调用配置了资源缓存策略,那么返回 Stage.RESOURCE_CACHE
//否则继续调用 Stage.RESOURCE_CACHE 执行
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE
: getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
//如果外部配置了源数据缓存,那么返回 Stage.DATA_CACHE
//否则继续调用 getNextStage(Stage.DATA_CACHE)
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE
: getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
//如果只能从缓存中获取数据,则直接返回 FINISHED,否则,返回SOURCE。
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
然后我们再来看看run中的这个方法:
java
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);
}
}
因为我们什么都没有配置,所以我们的资源执行器就是SourceGenerator:
然后我们就可以直接看下他的startNext方法:
java
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
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;
}
获取一个 ModelLoad 加载器,使用加载器中的 fetcher 根据优先级加载数据
走到DecoderHelp的buildLoadData:
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;
}
java
@Nullable
LoadData<Data> buildLoadData(
@NonNull Model model, int width, int height, @NonNull Options options);
boolean handles(@NonNull Model model);
下面是它的实现类 :
最终我们会在Httpurifetcher中找到它的网络请求,返回了InputStream
java
private InputStream loadDataWithRedirects(
URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
2.4 解压inputstream 采样压缩 得到bitmap 转换为Drawable
在DecodeJob中解析网络请求回来的数据:
java
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 {
// 调用 decodeFrom 解析数据HttpUrlFetcher , InputStream ,currentDataSource
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
//解析完成后,下发通知
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
具体怎么解析的,这里就不详细展开了,内容太庞大,大致步骤为:
1:deResource 将源数据解析成资源(源数据: InputStream, 中间产物: Bitmap)
2:调用 DecodeCallback.onResourceDecoded 处理资源
3:调用 ResourceTranscoder.transcode 将资源转为目标资源(目标资源类型: Drawable)
2.5 构建磁盘缓存 内存缓存
我们来看看下发通知的函数notifyEncodeAndRelease:
java
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
//通知调用层数据已经装备好了
notifyComplete(result, dataSource);
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();
}
这里就是将解析回来的资源存到了磁盘缓存。
然后通知上层来展示数据。
java
@SuppressWarnings("unchecked")
@Override
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?>
resource) {
if (resource != null) {
resource.setResourceListener(key, this);
//收到下游返回回来的资源,添加到活动缓存中
if (resource.isCacheable()) {
activeResources.activate(key, resource);
}
}
jobs.removeIfCurrent(key, engineJob);
}
然后添加到活动缓存中。
2.6 显示图片
当一切准备就绪的时候,最终会回到ImageViewTarget中去显示图片:
java
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
//显示资源
setResource(resource);
maybeUpdateAnimatable(resource);
}