Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。
目前,Glide的最新版本为4.2.0,本文是基于4.1.1这个版本来分析的,同属4.x,变化不大。
本文首发博客
基本用法
多数情况下,使用Glide加载图片非常简单,一行代码足矣:
Glide.with(fragment)
.load(myUrl)
.into(imageView);
取消加载同样很简单:
Glide.with(fragment).clear(imageView);
- asBitmap() //指定加载静态图片,如果是gif则加载第一帧。
- asGif() //如果是非gif,则加载失败。
- asXxx() // 较3.x新增了几个as方法。
注解生成流式API(与3.x版本最大区别)
Glide v4 使用 注解处理器 (Annotation Processor) 来生成出一个 API,在 Application 模块中可使用该流式 API 一次性调用到 RequestBuilder, RequestOptions 和集成库中所有的选项。
Generated API 模式的设计出于以下两个目的:
- 集成库可以为 Generated API 扩展自定义选项。
- 在 Application 模块中可将常用的选项组打包成一个选项在 Generated API 中使用
虽然以上所说的工作均可以通过手动创建 RequestOptions 子类的方式来完成,但想将它用好更具有挑战,并且降低了 API 使用的流畅性。
使用 Generated API
Generated API 默认名为 GlideApp ,与 Application 模块中 AppGlideModule的子类包名相同。在 Application 模块中将 Glide.with() 替换为 GlideApp.with(),即可使用该 API 去完成加载工作:
GlideApp.with(fragment)
.load(myUrl)
.placeholder(R.drawable.placeholder)
.fitCenter()
.into(imageView);
与 Glide.with() 不同,诸如 fitCenter() 和 placeholder() 等选项在 Builder 中直接可用,并不需要再传入单独的 RequestOptions 对象。
当然,Glide也支持kotlin,更多用法请参考官网。
Glide中文文档、英文文档
主要执行流程
本节主要从源码角度分析Glide的主体流程,相信阅读完本节内容,你对Glide会有更清晰的认识。
简易流程图
你可能现在看不太懂,没关系。下面会从源码角度分析Glide整个执行过程,完了之后,我们在回过来看也许就明白了。
由于Glide源码较复杂,阅读前最好明确目标,认准一个功能点,然后分析这个功能点如何实现即可,只追求主体实现逻辑,莫纠缠细节,点到为止。你说,我就想琢磨细节实现咋办?待下个回合将你这个细节作为目标带入分析,如此循环,各个击破。
以上,是我阅读源码的一些建议,希望对你有帮助。
下面,我们就以图作路,分析下面这句代码。
Glide.with(fragment).load(myUrl).into(imageView);
目标很明确。Glide是如何将这张图片加载并显示到组件上的? 从问题切入,到代码里找答案。
把图片加载并显示,这个过程,我理解就三步:
- 创建request
- 执行加载
- 回调刷新UI
创建request
获取RequestManager(初次会实例化Glide)
- 从Glide.with()方法开始
public static RequestManager with(Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
public static RequestManager with(Context context) {
return getRetriever(context).get(context);
}
//4.x新增
public static RequestManager with(View view) {
return getRetriever(view.getContext()).get(view);
}
可以看出,with方法重载种类多,值得说一点是,4.x新增了View参数的重载,这样便于在view类中使用。with方法比较简单,重载也是为了方便调用。
我们要知道RequestManager对象是怎么创建的?就先来看看getRetriever()方法。
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
/**
* Get the singleton.
*
* @return the singleton
*/
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
以上代码可以看出,RequestManagerRetriever对象通过Glide实例获取,而Glide实例是通过单利模式创建的,这里单利也是经典的“双重校验”模式。有关Glide实例化的细节,我们后面用到再讲。
那问题简单了,RequestManager对象就是通过RequestManagerRetriever的get方法创建并返回的。
//get方法也像with一样,有多个重载,这里只贴出一个代表性的。
public RequestManager get(Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
//num=0 这里如果是非主线程,直接返回applicationManager
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
//传入的Context为ApplicationContext
return getApplicationManager(context);
}
//get(Activity)/get(Fragment)逻辑差不多,这里分析以下这种类型。
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, null /*parentHint*/);
}
}
private RequestManager supportFragmentGet(Context context, FragmentManager fm,
Fragment parentHint) {
//num=1 获取managerFragment
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
//num=2 创建requestManager ,并传入了Lifecycle
requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode());
//num=3 缓存requestManager,保证一个Activity对应一个requestManager
current.setRequestManager(requestManager);
}
return requestManager;
}
//num=4 创建并添加一个SupportRequestManagerFragment
SupportRequestManagerFragment getSupportRequestManagerFragment(
final FragmentManager fm, Fragment parentHint) {
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingSupportRequestManagerFragments.put(fm, current);
//num=5 这里添加一个隐藏的Fragment
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
以上为RequestManagerRetriever类,我只贴了部分重要的代码。
可以看出get方法重载参数虽然很多,但最终就返回两种类型的requestManager。一种是ApplicationManager,它自动和应用的生命周期同步,应用退出,Glide也就停止加载;另外一种则是带有Fragment生命周期的requestManager。对应上述代码中 num=1-5注释,可以看出,Glide添加一个隐藏的Fragment,获取对应的生命周期回调事件,这样就可在Activity销毁时停止加载图片了。这种方式比较巧妙,不仅是Glide,RxPermission项目也是这样使用的。
到这里Glide.with()方法就分析完了。它主要完成了Glide实例化,并返回requestManager对象。
通过RequestBuilder创建Request
- 跟进load(url)方法
//这里默认调用的asDrawable,表示下载后要转换的类型。
//当然也可以设置其它类型,比如asBitmap、asFile,都类似,这里就不展开分析了。
public RequestBuilder load(@Nullable Object model) {
return asDrawable().load(model);
}
//最终后走到这里。
private RequestBuilder loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
load方法比较简单,根据传入的model类型对应有多个重载,但最终也只是将其缓存到model变量。这一节,我们是分析request的创建,现在到了RequestBuilder,看名字就知道是创建request的,哪它在哪里创建的呢?我们接着看into方法。
public Target into(ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
if (requestOptions.isLocked()) {
requestOptions = requestOptions.clone();
}
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions.optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions.optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions.optionalFitCenter();
break;
case FIT_XY:
requestOptions.optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
//以上逻辑是转换相关的,不看。只关注这句代码。
return into(context.buildImageViewTarget(view, transcodeClass));
}
//创建一个ImageViewTarget,用于后续更新UI
public Target buildImageViewTarget(ImageView imageView, Class transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public class ImageViewTargetFactory {
//根据类型返回target。因为load方法默认使用的是asDrawable,这里默认返回的是DrawableImageiewTarget。
@SuppressWarnings("unchecked")
public Target buildTarget(ImageView view, Class clazz) {
if (Bitmap.class.equals(clazz)) {
return (Target) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
//target创建好了,就接着看into(Y target)方法
public > Y into(@NonNull Y target) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
requestOptions.lock();
//这里创建的request
Request request = buildRequest(target);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& (Preconditions.checkNotNull(previous).isComplete()
|| Preconditions.checkNotNull(previous).isRunning())) {
request.recycle();
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
//这里执行request
requestManager.track(target, request);
return target;
}
//接着看buildRequest(target)
private Request buildRequest(Target target) {
return buildRequestRecursive(target, null, transitionOptions, requestOptions.getPriority(),
requestOptions.getOverrideWidth(), requestOptions.getOverrideHeight());
}
private Request buildRequestRecursive(Target target,
@Nullable ThumbnailRequestCoordinator parentCoordinator,
TransitionOptions, ? super TranscodeType> transitionOptions,
Priority priority, int overrideWidth, int overrideHeight) {
// 省略一大堆其它逻辑代码(缩放相关)
// Base case: no thumbnail.
return obtainRequest(target, requestOptions, parentCoordinator, transitionOptions, priority,
overrideWidth, overrideHeight);
}
private Request obtainRequest(Target target,
RequestOptions requestOptions, RequestCoordinator requestCoordinator,
TransitionOptions, ? super TranscodeType> transitionOptions, Priority priority,
int overrideWidth, int overrideHeight) {
requestOptions.lock();
//可以看到这里通过SingleRequest.obtain创建的Request,内部就是new一个SingleRequest对象并赋值相关属性,就不贴代码了。
// 但是需要搞清楚几个属性的值,我在下面代码中注释。
return SingleRequest.obtain(
context,
model,//对应load(myUrl),比如一个图片地址。
transcodeClass,//转换类型,这里默认对应Drawable.class
requestOptions,
overrideWidth,//宽
overrideHeight,
priority,
target,
requestListener,
requestCoordinator,
context.getEngine(),//全局加载引擎
transitionOptions.getTransitionFactory());
}
到这里Request的创建就分析完了,最终通过RequestBuilder生成了一个SingleRequest实例。这个SingleRequest类中有各种属性,大部分都是默认了,当然可以在使用时通过RequestOptions配置。简单回顾下。还是在那句代码贴过来。
Glide.with(fragment).load(myUrl).into(imageView);
咋一看都分析差不多了,但这只是假象。执行到into方法,流程刚刚开始,加载、缓存、转换等逻辑都在后面。so,我们继续。
执行数据加载
我们先回到RequestBuilder类中into(Y target)方法,也是执行加载的入口。
public > Y into(@NonNull Y target) {
... 省略
requestManager.clear(target);
target.setRequest(request);
//跟进manager的track方法
requestManager.track(target, request);
}
void track(Target> target, Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
//以下是SingleRequest类中begin方法实现。
@Override
public void begin() {
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
//num=0
if (model == null) {
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
//num=3
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
//num=1
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
//num=2
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
以上代码分别来至几个类。很容易看出执行流程:into()=>track()=>runRequest()=>begin(),这里分析下begin方法。
-
num=0(对应代码中注释num=0处)
可以看到判断model变量为null,就回调onLoadFailed方法,这个方法就会设置我们配置的error placeholder资源。这里的model变量就是我们通过load(myUrl)方法传入的图片地址。
-
num=1
这里主要是判断overrideWidth, overrideHeight是否可用。分两种情况:1.如果设置了override(int width, int height) ,直接处理onSizeReady方法逻辑。2.没有设置override,Glide就会等到系统计算完组件宽高后再回调onSizeReady。所以两种情况最后都会调用onSizeReady方法。
-
num=2
开始前,回调设置placeholderDrawable,和num=0类似。
-
num=3
加载完成回调。这里是加载、缩放、转换之后的数据,可直接用于UI显示。后面再分析是怎么回调刷新UI的。
到这里,默认流程下一步就会走到onSizeReady方法。
@Override
public void onSizeReady(int width, int height) {
...省略
//Engine类的load方法,正式步入加载流程。
loadStatus = engine.load(
glideContext,
model,//对应myUrl,图片地址
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),//默认是Object.class
transcodeClass, //默认Drawbale.class
priority,
requestOptions.getDiskCacheStrategy(),//缓存策略。默认是AUTOMATIC
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
}
可以看到,调用了Engine的load方法。重点来了。
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 onlyRetrieveFromCache,
ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
//生成缓存key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//num=4 从内存缓存中读取
EngineResource> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
//num=5 如果上一步没有命中,则从ActiveResource中读取。
EngineResource> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
//囧,也没命中。检查当前Request是否正在执行。
EngineJob> current = jobs.get(key);
if (current != null) {
//不为null,缓存回调函数,等待完成后执行。
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//num=6 到这里,就需要创建后台任务执行了。
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable,
useUnlimitedSourceExecutorPool);
DecodeJob 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);
//num=7 开启任务。
engineJob.start(decodeJob);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
-
先说一下Glide的缓存策略
默认情况下,Glide 会在开始一个新的图片请求之前检查以下多级的缓存:
- 活动资源 (Active Resources) - 正在显示的资源
- 内存缓存 (Memory cache) - 显示过的资源
- 资源类型(Resource) - 被解码、转换后的资源
- 数据来源 (Data) - 源文件(未处理过)资源
其实也就是内存缓存+磁盘缓存。
以上代码中 注释:num=4、5 处,表示从内存缓存中获取资源,如果命中,直接返回,没命中则创建任务并执行(对应 注释:num=6、7 )。本文主要分析工作流程,缓存部分就不细说了。
我们接着往下看,DecodeJob内部到底做了什么?,可以说DecodeJob是整个流程中的重点,也是我认为设计得很巧妙地方。
//对应上一段代码中num=7处,执行EngineJob的start方法
//start方法就是根据diskCacheStrategy策略获取一个executor来执行DecodeJob
public void start(DecodeJob decodeJob) {
this.decodeJob = decodeJob;
//这里根据缓存策略,决定使用哪个Executor。默认情况返回diskCacheExecutor。
//共三种执行器:diskCacheExecutor、sourceExecutor、sourceUnlimitedExecutor对应文章前面给出的流程图。
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
//当然,DecodeJob实现了Runnable接口。直接来看它的run方法。
@Override
public void run() {
TraceCompat.beginSection("DecodeJob#run");
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();//看这里!
} catch (RuntimeException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "DecodeJob threw unexpectedly"
+ ", isCancelled: " + isCancelled
+ ", stage: " + stage, e);
}
// When we're encoding we've already notified our callback and it isn't safe to do so again.
if (stage != Stage.ENCODE) {
notifyFailed();
}
if (!isCancelled) {
throw e;
}
} finally {
if (currentFetcher != null) {
currentFetcher.cleanup();
}
TraceCompat.endSection();
}
}
//接着看runWrapped方法。
//RunReason是一个枚举,默认值为INITIALIZE。区分任务目的。
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);
}
}
//获取任务执行阶段:初始化、读取转换后的缓存、读取原文件缓存、原文件加载、结束状态。
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);
}
}
//根据上一个方法确定的stage,创建对应的Generator(可把它简单理解成资源加载器)
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;
}
}
// 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.
}
@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);
}
以上代码,为方便理解,我加了部分注释。这里再捋一捋思路和逻辑。从最开始没有命中内存缓存开始,然后执行Engine的start方法,默认情况会获取到cacheExecutor执行器来执行decodeJob任务;继续decodeJob的run方法,因为RunReason==INITIALIZE,接着获取stage,默认会返回Stage.RESOURCE_CACHE,这时通过getNextGenerator就返回了ResourceCacheGenerator加载器,紧接着就是调用 ResourceCacheGenerator的startNext方法 ,从转换后的缓存中读取已缓存的资源,如果命中则结束任务并回调结果,反之,任务切换到DataCacheGenerator加载器继续执行,若还是未命中,则切换到SourceGenerator加载器(第一次加载,由于没有任何缓存,就会走到这里),这时会通过任务调度,将线程运行环境切换到 SourceExecutor执行器来执行,最后,待SourceGenerator加载完成后结束任务,回调结果,流程结束。
现在,我们再来看看这张流程图。是不是已经有了一定的理解,不明白之处,请结合上文和源码继续研究吧。
你可能会问,Glide执行流程就分析完了? 加载网络图片的代码逻辑都没看到啊? 按照只分析主流程的思路,点到为止,以上内容就算是分析完了。但相信很多同学都想知道 加载网络图片代码逻辑到底在哪里?Glide是怎么调用这块代码的? 面对这两个问题,我们就继续吧。
这里就需要从Glide实例初始化开始说起。我们来看下Glide的构造方法。
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
Glide(
Context context,
Engine engine,
MemoryCache memoryCache,
BitmapPool bitmapPool,
ArrayPool arrayPool,
RequestManagerRetriever requestManagerRetriever,
ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
RequestOptions defaultRequestOptions,
Map, TransitionOptions, ?>> defaultTransitionOptions) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
final Resources resources = context.getResources();
//重点看register类,这里调用了各种append、register、prepend等方法。其实就是相关功能类的注册过程。
registry = new Registry();
registry.register(new DefaultImageHeaderParser());
Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
resources.getDisplayMetrics(), bitmapPool, arrayPool);
ByteBufferGifDecoder byteBufferGifDecoder =
new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);
registry.register(ByteBuffer.class, new ByteBufferEncoder())
.register(InputStream.class, new StreamEncoder(arrayPool))
/* Bitmaps */
.append(ByteBuffer.class, Bitmap.class,
new ByteBufferBitmapDecoder(downsampler))
.append(InputStream.class, Bitmap.class,
new StreamBitmapDecoder(downsampler, arrayPool))
.append(ParcelFileDescriptor.class, Bitmap.class, new VideoBitmapDecoder(bitmapPool))
.register(Bitmap.class, new BitmapEncoder())
/* GlideBitmapDrawables */
.append(ByteBuffer.class, BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, bitmapPool,
new ByteBufferBitmapDecoder(downsampler)))
.append(InputStream.class, BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, bitmapPool,
new StreamBitmapDecoder(downsampler, arrayPool)))
.append(ParcelFileDescriptor.class, BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, bitmapPool, new VideoBitmapDecoder(bitmapPool)))
.register(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, new BitmapEncoder()))
/* GIFs */
.prepend(InputStream.class, GifDrawable.class,
new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))
.prepend(ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
.register(GifDrawable.class, new GifDrawableEncoder())
/* GIF Frames */
.append(GifDecoder.class, GifDecoder.class, new UnitModelLoader.Factory())
.append(GifDecoder.class, Bitmap.class, new GifFrameResourceDecoder(bitmapPool))
/* Files */
.register(new ByteBufferRewinder.Factory())
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, File.class, new FileDecoder())
.append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
.append(File.class, File.class, new UnitModelLoader.Factory())
/* Models */
.register(new InputStreamRewinder.Factory(arrayPool))
.append(int.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
.append(
int.class,
ParcelFileDescriptor.class,
new ResourceLoader.FileDescriptorFactory(resources))
.append(Integer.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
.append(
Integer.class,
ParcelFileDescriptor.class,
new ResourceLoader.FileDescriptorFactory(resources))
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
.append(
Uri.class,
ParcelFileDescriptor.class,
new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
.append(
Uri.class,
InputStream.class,
new UriLoader.StreamFactory(context.getContentResolver()))
.append(Uri.class, ParcelFileDescriptor.class,
new UriLoader.FileDescriptorFactory(context.getContentResolver()))
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
.append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
.append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
.append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
.append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
/* Transcoders */
.register(Bitmap.class, BitmapDrawable.class,
new BitmapDrawableTranscoder(resources, bitmapPool))
.register(Bitmap.class, byte[].class, new BitmapBytesTranscoder())
.register(GifDrawable.class, byte[].class, new GifDrawableBytesTranscoder());
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
glideContext =
new GlideContext(
context, registry, imageViewTargetFactory, defaultRequestOptions,
defaultTransitionOptions, engine, logLevel);
}
以上代码可以看出,完成对registry对象的各种功能类注册(这种设计提高了其扩展性),太多了有木有眼花,各种loader、encoder、decoder、transcoder等等,带着问题我们只分析和网络图片加载相关的。
.append(String.class, InputStream.class, new StringLoader.StreamFactory()
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
以上代码可以看出,分别对String.class、Uri.class、GlideUrl.class三种类型注入了不同的Factory,这个Factory使用创建ModelLoader的,ModelLoader就是用来加载图片的。说的不是很清楚,这里只需要明白,registry分别对这三种类型注册了生成ModelLoader的工厂类。
接着,进入Registry类中,看看怎么缓存这些功能类的。
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 Registry append(Class modelClass, Class dataClass,
ModelLoaderFactory factory) {
modelLoaderRegistry.append(modelClass, dataClass, factory);
return this;
}
...
}
//继续跟进代码。ModelLoaderRegistry类中
public synchronized void append(Class modelClass, Class dataClass,
ModelLoaderFactory factory) {
multiModelLoaderFactory.append(modelClass, dataClass, factory);
cache.clear();
}
//最后进入MultiModelLoaderFactory类中的add方法
private void add(Class modelClass, Class dataClass,
ModelLoaderFactory factory, boolean append) {
Entry entry = new Entry<>(modelClass, dataClass, factory);
//entries是一个list。所以,到这里就知道注册的LoaderFactory被缓存到了列表中,以便后面取用。
entries.add(append ? entries.size() : 0, entry);
}
通过以上代码分析,知道了ModelLoaderFactory在Glide实例化时被注册到了一个列表中,以待用时获取。 在分析DecodeJob代码逻辑时,我们知道SourceGenerator是加载图片资源的,下面我们就看下SourceGenerator是怎么获取上面注册的ModelLoader并完成数据加载的。
//DecodeJob调用SourceGenerator的startNext方法执行任务。
@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()) {
//num=8
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
//num=9 这里是真正调用fetcher.loadData方法加载数据的地方。
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
以上代码 num=9 注释处,可以看出loadData对象是通过helper.getLoadData()返回并在while中条件筛选得到。接着看下这个helper类是什么来头。
首先DecodeHelper是在DecodeJob中实例化的。
//DecodeJob类中,decodeHelper调用init方式的代码片段
decodeHelper.init(
glideContext,
model, //String类型,对应load(myUrl)中的myUrl.
signature,
width,
height,
diskCacheStrategy,
resourceClass, //默认是Object.class
transcodeClass, //默认是Drawable.class
priority,
options,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
diskCacheProvider);
知道model变量的类型,对获取ModelLoader逻辑理解很重要。现在我们去DecodeHelper类中查看getLoadData方法。(对应上一个代码块中 num=8注释处 )
List> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
//根据model类型,通过Glide对应Registry获取ModelLoader列表。
List> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
int size = modelLoaders.size();
for (int i = 0; i < size; i++) {
ModelLoader
到这里,我们再理一下思路。资源加载器SourceGenerator,使用特定model类型,通过Register获取已经注册过的ModelLoader列表,当然这个ModelLoader是通过注册的xxxFactory.build而来,拿到ModelLoader列表后,在通过modelLoader.buildLoadData方法转化为LoadData对象列表,LoadData对象持有一个DataFetcher引用,最后就是在加载器SourceGenerator中调用以下代码执行加载数据。
loadData.fetcher.loadData(helper.getPriority(), this);
再来看之前注入的三个loaderFactory
.append(String.class, InputStream.class, new StringLoader.StreamFactory()
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
告诉大家,通过Glide.with(this).load(myUrl).into(view),真正加载网络图片对应的loader是HttpGlideUrlLoader。先来看下为什么是它?明明传入的是String而非GlideUrl。
按照类型注册,那匹配会先获取到StringLoader.StreamFactory。
public class StringLoader implements ModelLoader {
private final ModelLoader uriLoader;
public StringLoader(ModelLoader uriLoader) {
this.uriLoader = uriLoader;
}
@Override
public LoadData buildLoadData(String model, int width, int height,
Options options) {
Uri uri = parseUri(model);
return uri == null ? null : uriLoader.buildLoadData(uri, width, height, options);
}
/**
* Factory for loading {@link InputStream}s from Strings.
*/
public static class StreamFactory implements ModelLoaderFactory {
@Override
public ModelLoader build(MultiModelLoaderFactory multiFactory) {
//num=10
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
}
在 num=10 注释处,MultiModelLoaderFactory通过Uri.class和InputStream.class创建一个ModelLoader给StringLoader,所以StringLoader的加载功能转移了。而且根据注册关系知道转移到了HttpUriLoader中。
public class HttpUriLoader implements ModelLoader {
private static final Set SCHEMES =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));
private final ModelLoader urlLoader;
public HttpUriLoader(ModelLoader urlLoader) {
this.urlLoader = urlLoader;
}
@Override
public LoadData buildLoadData(Uri model, int width, int height, Options options) {
return urlLoader.buildLoadData(new GlideUrl(model.toString()), width, height, options);
}
@Override
public boolean handles(Uri model) {
return SCHEMES.contains(model.getScheme());
}
/**
* Factory for loading {@link InputStream}s from http/https {@link Uri}s.
*/
public static class Factory implements ModelLoaderFactory {
@Override
public ModelLoader build(MultiModelLoaderFactory multiFactory) {
//num=11 MD又转移了,这次转移到了HttpGlideUrlLoader
return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
}
//跟进HttpGlideUrlLoader类
public class HttpGlideUrlLoader implements ModelLoader {
/**
* An integer option that is used to determine the maximum connect and read timeout durations (in
* milliseconds) for network connections.
*
* Defaults to 2500ms.
*/
public static final Option TIMEOUT = Option.memory(
"com.bumptech.glide.load.model.stream.HttpGlideUrlLoader.Timeout", 2500);
@Nullable private final ModelCache modelCache;
public HttpGlideUrlLoader() {
this(null);
}
public HttpGlideUrlLoader(ModelCache modelCache) {
this.modelCache = modelCache;
}
@Override
public LoadData buildLoadData(GlideUrl model, int width, int height,
Options options) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
// spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
//创建LoadData,新建的HttpUrlFetcher
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
@Override
public boolean handles(GlideUrl model) {
return true;
}
/**
* The default factory for {@link HttpGlideUrlLoader}s.
*/
public static class Factory implements ModelLoaderFactory {
private final ModelCache modelCache = new ModelCache<>(500);
@Override
public ModelLoader build(MultiModelLoaderFactory multiFactory) {
//可以看出HttpGlideUrlLoader打算自己处理
return new HttpGlideUrlLoader(modelCache);
}
@Override
public void teardown() {
// Do nothing.
}
}
}
到这里就知道了,load(myUrl) 实际加载是使用的HttpGlideUrlLoader,对应的Fetcher就是HttpUrlFetcher。
最后贴下HttpUrlFetcher代码
/**
* A DataFetcher that retrieves an {@link java.io.InputStream} for a Url.
*/
public class HttpUrlFetcher implements DataFetcher {
public HttpUrlFetcher(GlideUrl glideUrl, int timeout) {
this(glideUrl, timeout, DEFAULT_CONNECTION_FACTORY);
}
// Visible for testing.
HttpUrlFetcher(GlideUrl glideUrl, int timeout, HttpUrlConnectionFactory connectionFactory) {
this.glideUrl = glideUrl;
this.timeout = timeout;
this.connectionFactory = connectionFactory;
}
@Override
public void loadData(Priority priority, DataCallback super InputStream> callback) {
long startTime = LogTime.getLogTime();
final InputStream result;
try {
result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,
glideUrl.getHeaders());
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
return;
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)
+ " ms and loaded " + result);
}
callback.onDataReady(result);
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map 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 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();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == -1) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}
}
历经千山万水,,终于看到了网络通讯代码。比较简单,就是通过HttpURLConnection获取数据流并返回。当然你也可以使用Okhttp来加载,具体用法请查询官网。
到这里,已经分析完Glide的整个加载过程,剩下就简单说下回调刷新UI部分。
回调刷新UI
回到DecodeJob类中
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher> fetcher,
DataSource dataSource, Key attemptedKey) {
...
//资源加载完成后回调,执行
decodeFromRetrievedData();
...
}
/处理源数据
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
Resource resource = null;
try {
//比如缩放,转换等。会判断currentDataSource类型。
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
exceptions.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private void notifyEncodeAndRelease(Resource resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource result = resource;
LockedResource 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();
}
onEncodeComplete();
}
}
private void notifyComplete(Resource resource, DataSource dataSource) {
setNotifiedOrThrow();
//这里的callback就是EngineJob
callback.onResourceReady(resource, dataSource);
}
以上代码看出,DecodeJob加载完数据后,会做转换、缓存等操作,这些咱不细究,关注回调流程即可。
//来到EngineJob类中
@Override
public void onResourceReady(Resource resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
//将回调过程通过Handler切换到主线程
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
@Override
public boolean handleMessage(Message message) {
EngineJob> job = (EngineJob>) message.obj;
switch (message.what) {
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
case MSG_EXCEPTION:
job.handleExceptionOnMainThread();
break;
case MSG_CANCELLED:
job.handleCancelledOnMainThread();
break;
default:
throw new IllegalStateException("Unrecognized message: " + message.what);
}
return true;
}
@Synthetic
void handleResultOnMainThread() {
stateVerifier.throwIfRecycled();
if (isCancelled) {
resource.recycle();
release(false /*isRemovedFromQueue*/);
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of
// notifying if it synchronously released by one of the callbacks.
engineResource.acquire();
//通知并缓存到activeResources(内存缓存中的一种)中。
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
//这里将回调到SingleRequest的onResourceReady中。
cb.onResourceReady(engineResource, dataSource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
release(false /*isRemovedFromQueue*/);
}
//继续跟进SingleRequest类
@Override
public void onResourceReady(Resource> resource, DataSource dataSource) {
...
onResourceReady((Resource) resource, (R) received, dataSource);
}
private void onResourceReady(Resource resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (requestListener == null
|| !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource)) {
Transition super R> animation =
animationFactory.build(dataSource, isFirstResource);
//如果没有设置requestListener或者未消耗事件,就会回调target的onResourceReady方法。
//默认的target是DrawableImageViewTarget
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
}
从以上代码得知,回调从Decodejob出来,在EngineJob中切换到主线程并一路回调到DrawableImageViewTarget中,至于为什么默认是DrawableImageViewTarget,请查看RequestBuilder中into方法。下面我们再看下DrawableImageViewTarget相关代码,也是设置显示图片的地方。
public class DrawableImageViewTarget extends ImageViewTarget {
public DrawableImageViewTarget(ImageView view) {
super(view);
}
@Override
protected void setResource(@Nullable Drawable resource) {
//实现了该方法。简单将drawable设置给imageview。
view.setImageDrawable(resource);
}
}
public abstract class ImageViewTarget extends ViewTarget
implements Transition.ViewAdapter {
public ImageViewTarget(ImageView view) {
super(view);
}
@Override
public void onResourceReady(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) {
maybeUpdateAnimatable(resource);
setResource(resource);
}
private void maybeUpdateAnimatable(@Nullable Z resource) {
if (resource instanceof Animatable) {
animatable = (Animatable) resource;
animatable.start();
} else {
animatable = null;
}
}
protected abstract void setResource(@Nullable Z resource);
}
可以看到,onResourceReady在父类ImageViewTarget中回调,然后调用setResource将图片设置并显示出来。代码执行到这里,回调过程也就完了。Glide的整个执行流程也分析完了。
总结
图片加载相关开源项目也有好几个(Picasso、Fresco、ImageLoader),为啥Glide能占一席之地。有几点:1.Google 推荐, 2. 专注平滑的滚动, 3. 简单易用的API , 4.高性能、可扩展。
经过这段时间的学习,我发现,Glide先通过简单易用吸引你,阅读源码后发现其功能之强大,所以可见代码设计、封装均是上佳之作;然后又发现,其功能扩展也能随心所欲,这里推荐一个图片转换库glide-transformations;源码中,我比较欣赏Decodejob类相关部分设计,功能无缝切换,线程调度自然。总之,Glide源码值得你用心拜读。
杂谈
-
文章太长
虽然一直在强调只分析主流程,不关心细节,但还是写了怎么多,囧~。后面得注意了,毕竟长篇幅,问题难免考虑周全,也不易于阅读。
-
RTFSC (Read the fucking source code )
遇到问题,第一时间想到的应该是阅读源码。
-
将知识点与自己连接起来
我以前一直有个问题,看到喜欢的文章就会收藏,想的是后面慢慢看,或者用到的时候能找到就行;但久了发现收藏的文章基本和自己没啥关系,什么用到的时候能找到?别想,基本能全忘记,啥印象都没。但现在看到好文章,我至少得评论下,甚至花时间梳理,这样对知识点就有了连接,才有可能日后为你所用。
看到这里了,好厉害。快给自己和文章点个赞吧。
限于水平有限,文中定有错误和疏漏之处,恳请赐教。若有不明白之处,欢迎随时评论交流。
参考
- Glide项目地址
- Glide中文文档、英文文档
- Glide最全解析系列文章 - 郭神基于3.7.0版本分析的,通俗易懂,推荐阅读。