接上文重学Android——Glide4.x源码分析(1)html
接上一文,昨天讲到图片加载,最终调用到了onSizeReady的方法,调用了其中的engine.load方法java
@Override
public synchronized void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
...
status = Status.RUNNING;
//计算缩略图的尺寸
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
//加载流程
loadStatus =
engine.load(
glideContext,
model,//对应rul,图片地址
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),//默认是Object.class
transcodeClass,//默认是Drawable.class
priority,
requestOptions.getDiskCacheStrategy(),//缓存策略,默认是AUTOMATIC
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
}
复制代码
咱们再直接跟进Engin.load方法缓存
public synchronized <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;
//1建立资源索引key,内存缓存的惟一键值
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
//2首先从内存中找当前正在显示的资源缓存
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
//3没有找到,就从内存缓存资源中加载图片
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
//4仍是没找到,若是这个任务已经在队列中,获取已经存在的加载任务
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);
}
//5尚未找到,若是是新建加载任务,建立EngineJob和DecodeJob,而后开始任务
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
//6新建解码任务,真正执行数据加载和解码的类
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
//7缓存加载任务
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);
}
复制代码
这个方法代码有点长,倒是Glide缓存机制的核心,先说一下几个名词:bash
活动资源Active Resources 正在显示的资源服务器
内存缓存Memory cache 显示过的资源网络
资源类型Resources 被解码、转换后的资源app
数据来源Data 源文件(未处理过的资源)框架
能够看到,这其实就是磁盘+内存+网络三级缓存!ide
再来分析一下上面load方法的执行流程:oop
因此咱们能够直接到engineJob.start()方法
//start方法就是根据diskCacheStrategy策略获取一个executor来执行DecodeJob
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
//这里根据缓存策略,决定使用哪个Executor,默认状况返回DiskCacheExecutor
//共有三种执行器,diskcacheExecutor,sourceExecutor,sourceUnlimitedExecutor
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
复制代码
能够看到,在start方法直接启用了线程池的execute方法,能够知道,DecodeJob类必定是个runnable,咱们去看它的run方法。
//DecodeJob.java
@Override
public void run() {
...
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
//重点在这
runWrapped();
} catch (CallbackException e) {
...
}
}
复制代码
若是一切正常,那么会进入runWrapped方法
private void runWrapped() {
switch (runReason) {
//默认状态为INITISLIZE
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);
}
}
复制代码
默认的状态是INITIALIZE,那咱们来看它调用的几个方法
//获取任务执行阶段:初始化,读取转换后的缓存,读取原文件(未处理)缓存,远程图片加载,结束状态
private Stage getNextStage(Stage current) {
switch (current) {
//初始状态
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
//状态2,读取转换后的缓存
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
//状态3,读取原文件缓存
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);
}
}
//这里开始加载执行
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
//这里Generator.startNext方法中就是加载过程,若是成功就返回true并跳出循环,不然切换Generator继续执行
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
//若是任务执行到去远程加载,且切换任务执行环境
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
@Override
public void reschedule() {
//更改执行目标为:SOURCE服务。固然也只有在stage == Stage.SOURCE的状况下会被调用。
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);//这里callback正是EngineJob。
}
//代码跟进EngineJob类中,能够看到实现方法。
@Override
public void reschedule(DecodeJob<?> job) {
// 能够看到,这里获取的SourceExecutor来执行decodeJob。
//也就巧妙地将此decodeJob任务从cacheExecutor切换到了SourceExecutor,这样分工协做更加高效。
getActiveSourceExecutor().execute(job);
}
复制代码
能够看到,这里几个方法构成了Glide的解码流程:尝试从转换过的本地资源加载图片;尝试从没有处理过的原始资源中加载图片;尝试远程加载图片。经过状态的切换来寻找下一个加载器,直到加载到这张图,返回成功,若是找不到,返回失败。
再捋一捋:
从上面咱们能够知道,SourceGenerator能够加载网络与本地图片,那么咱们直接看SourceGenerator调用startNext方法
/** * SourceGenerator * DataFetcher的简介:Fetcher的意思是抓取,因此该类能够称为数据抓取器 * 做用就是根据不一样的数据来源(本地,网络,Asset等) * 以及读取方式(Stream,ByteBuffer等)来提取并解码数据资源,实现类以下 * AssetPathFetcher:加载Asset数据 * HttpUrlFetcher:加载网络数据 * LocalUriFetcher:加载本地数据 * 其余实现类... * */
@Override
public boolean startNext() {
//忽略缓存部分逻辑
...
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
//是否有更多的ModelLoader
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
//选择合适的LoadData,并使用fetcher来抓取数据
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
复制代码
继续跟进代码,private volatile ModelLoader.LoadData<?> loadData,是一个接口,就须要咱们来找出它的实现类了
回到Glide的初始化中
Glide(...) {
...
registry
...
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
....
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
...
.register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptions,
defaultTransitionOptions,
defaultRequestListeners,
engine,
isLoggingRequestOriginsEnabled,
logLevel);
}
复制代码
这构造方法是很长的,咱们只看咱们关心的网络相关的部分,能够看到,咱们将String.class Uri.class GlideUri.class三种类型注入了不一样的Factory,这个Factory就是用来建立ModelLoader的,ModelLoader就是用来加载图片的。
public class Registry {
//各类功能类注册器。加载、转换、解码、加密等。
private final ModelLoaderRegistry modelLoaderRegistry;
private final EncoderRegistry encoderRegistry;
private final ResourceDecoderRegistry decoderRegistry;
private final ResourceEncoderRegistry resourceEncoderRegistry;
private final DataRewinderRegistry dataRewinderRegistry;
private final TranscoderRegistry transcoderRegistry;
private final ImageHeaderParserRegistry imageHeaderParserRegistry;
...
//modelLoader注册
public <Model, Data> Registry append(Class<Model> modelClass, Class<Data> dataClass, ModelLoaderFactory<Model, Data> factory) {
modelLoaderRegistry.append(modelClass, dataClass, factory);
return this;
}
...
}
//继续跟进代码,ModelLoaderRegistry类中
public synchronized <Model, Data> void append(Class<Model> modelClass, Class<Data> dataClass, ModelLoaderFactory<Model, Data> factory) {
multiModelLoaderFactory.append(modelClass, dataClass, factory);
cache.clear();
}
//最后进入MultiModelLoaderFactory类中的add方法
private <Model, Data> void add(Class<Model> modelClass, Class<Data> dataClass, ModelLoaderFactory<Model, Data> factory, boolean append) {
Entry<Model, Data> entry = new Entry<>(modelClass, dataClass, factory);
//entries是一个list。因此,到这里就知道注册的LoaderFactory被缓存到了列表中,以便后面取用。
entries.add(append ? entries.size() : 0, entry);
}
复制代码
经过以上代码,知道了ModelLoaderFactory在Glide初始化时注册到了一个列表中,之后使用。在分析DecodeJob的代码里时,咱们使用SourceGenerator加载远程图片,并分析到了loadData.fetcher.loadData(helper.getPriority(), this);
是真正加载数据的地方。
咱们先看loadData在哪赋值的
loadData = helper.getLoadData().get(loadDataListIndex++);
复制代码
能够看到,是使用Helper来调用方法。
//DecodeHelper
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
//根据model类型,经过Glide对应的registry获取ModelLoader列表
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);
//循环建立出LoadData,用户后面加载
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
复制代码
能够看到,这实际上是一个列表,能够知道,在
.append(String.class, InputStream.class, new StringLoader.StreamFactory()
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
复制代码
这三个Factory中,由于咱们load的是一个String,因此咱们确定是StringLoader中
public class StringLoader<Data> implements ModelLoader<String, Data> {
private final ModelLoader<Uri, Data> uriLoader;
// Public API.
@SuppressWarnings("WeakerAccess")
public StringLoader(ModelLoader<Uri, Data> uriLoader) {
this.uriLoader = uriLoader;
}
@Override
public LoadData<Data> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
Uri uri = parseUri(model);
if (uri == null || !uriLoader.handles(uri)) {
return null;
}
return uriLoader.buildLoadData(uri, width, height, options);
}
...
/** * Factory for loading {@link InputStream}s from Strings. */
public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
@NonNull
@Override
public ModelLoader<String, InputStream> build( @NonNull MultiModelLoaderFactory multiFactory) {
//关键在这儿
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
}
复制代码
能够看到,其实它在里面MultiModelLoaderFactory经过Uri.class和InputStream.class建立一个ModelLoader给StringLoader,因此StringLoader的加载功能转移了。并且根据注册关系知道转移到了HttpUriLoader中。而它对应的Fetcher就是HttpUrlFetcher。
那么能够直接看HttpUrlFetcher的load代码
/** * HttpUrlFetcher * HttpUrlFetcher的简介:网络数据抓取器,通俗的来说就是去服务器上下载图片,支持地址重定向(最多5次) * */
@Override
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) {
...
}
}
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.
//经过URL的equals方法来比较会致使网络IO开销,通常会有问题
//能够参考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.
}
}
//下面开始,终于看到了可爱的HttpUrlConnection下载图片
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);
}
}
复制代码
看到这,终于找到了网络通信的代码,就是经过HttpUrlConnection来获取数据流并返回。固然也能够自定义使用OkHttp。能够参考Glide4.8集成现有OkHttpClient并加载https图片blog.csdn.net/ysy950803/a…
分析源码的时候,咱们只应该关注主流程,不要太过于纠结每个细节。Glide的源码很是值得仔细反复阅读,每次都能学到很多东西,固然也是一个考验耐心的事情。
参考
Glide最全解析系列文章 - 郭神基于3.x版本的。
下面是个人公众号,欢迎你们关注我