图片回调流程
前面的文章详细介绍了Glide加载图片的流程,这里我们重点回顾一下图片的的显示流程:DecodeJob完成图片的装载之后,会回调到notifyEncodeAndRelease()方法,之后的流程如下。
- EngineJob.onResourceReady
- EngineJob.notifyCallbacksOfResult
- EngineJob.callCallbackOnResourceReady
- ResourceCallback.onResourceReady
- SingleRequest.onResourceReady
- ImageViewTarget.onResourceReady
- ImageViewTarget.setResource
- view.setImageDrawable(resource);
上面的流程可以看到,图片展示的地方是在Target的onResourceReady()中执行的。这里的Target是哪个步骤构建的呢?回顾一下前面的文章流程中可以知道,在into()方法中,会构建Target,在Request的构建中,Target作为参数传进Request中。
自定义Target的原理和实现
面的图片回调流程基本上就介绍了Target的原理,自定义Target的时候,只需要实现onResourceReady()方法中展示图片即可,使用定义Target的用法如下:1
2
3
4
5
6
7
8SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() {
public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
imageView.setImageDrawable(resource);
}
};
Glide.with(this).load(url).into(simpleTarget);
以上是基本的自定义Target的用法,掌握了图片的显示流程就可以定义复杂的Target,实现复杂的图片加载需求。
Preload的功能和原理
preload()可以替换into()法的另外一个方法,和into()不同的是,preload()方法只加载图片,而不显示图片,是一种图片预加载的功能,使真正显示图片的时候不需要从网络获取,提高图片的显示速度。preload()方法是如何实现不加载图片的呢?通过前面的图片加载流程,我们知道,获取图片之后DecodeJob会执行图片的显示流程,而图片显示是Target完成的。之前的文章分析Glide加载图片的流程的时候我们知道into()方法,Glide内部逻辑中会构建一个Target,这个Target就是显示图片的对象。我们需要分析preload()内部的Target是如何不显示图片的。
1 | public Target<TranscodeType> preload() { |
我们可以看到:preload()方法会先构建一个PreloadTarget对象,然后调用into(targe)。所以PreloadTarget类完成了图片不显示的逻辑。我们分析一下PreloadTarget的实现:
1 | private static final Handler HANDLER = new Handler(Looper.getMainLooper(), new Callback() { |
可以看到PreloadTarget内部在onResourceReady()中仅仅是发送了一个message,并没有显示图片。需要注意的是preload()默认是全尺寸缓存图片的,使用into()显示preoload()已经预加载的图片时,需要指定缓存策略为DiskCacheStrategy.SOURCE,否则into()会找不到缓存,从网络加载图片。
downloadOnly的功能和原理
into()和preload()方法都是加载图片的操作,不提供图片的路径信息,开发者只关心图片显示的问题,对图片本身不关注,如果我们需要对图片本身的信息做处理的话,就需要知道图片的保存路径,Glide提供了两个方法可以获取图片的路径:
- downloadOnly(int width, int height)
- downloadOnly(Y target)
downloadOnly(int width, int height)方法,该方法主要完成图片加载不显示,与preload功能相似,同时他提供了一个获取图片缓存路径的方法,该方法是阻塞方法,如果图片没有下载成功,会阻塞,因此一般使用get()方法需要在子线程中调用,同时get()内部也会检查是否在子线程,否则抛异常。
downloadOnly(Y target)不同的是不需要再子线程中运行。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27protected static final RequestOptions DOWNLOAD_ONLY_OPTIONS =
new RequestOptions().diskCacheStrategy(DiskCacheStrategy.DATA).priority(Priority.LOW) .skipMemoryCache(true);
public FutureTarget<File> downloadOnly(int width, int height) {
return getDownloadOnlyRequest().submit(width, height);
}
protected RequestBuilder<File> getDownloadOnlyRequest() {
return new RequestBuilder<>(File.class,this).apply(DOWNLOAD_ONLY_OPTIONS);
}
public FutureTarget<TranscodeType> submit(int width, int height) {
final RequestFutureTarget<TranscodeType> target =
new RequestFutureTarget<>(glideContext.getMainHandler(), width, height);
if (Util.isOnBackgroundThread()) {
glideContext.getMainHandler().post(new Runnable() {
public void run() {
if (!target.isCancelled()) {
into(target, target);
}
}
});
} else {
into(target, target);
}
return target;
}
可以看到downloadOnly(int width, int height)内部构建的Target是RequestFutureTarget,图片的不显示功能和get()图片缓存文件都是在这里提供的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 public synchronized void onResourceReady(R resource, Transition<? super R> transition) {
// Ignored, synchronized for backwards compatibility.
}
public R get() throws InterruptedException, ExecutionException {
try {
return doGet(null);
} catch (TimeoutException e) {
throw new AssertionError(e);
}
}
private synchronized R doGet(Long timeoutMillis)
throws ExecutionException, InterruptedException, TimeoutException {
if (assertBackgroundThread && !isDone()) {
Util.assertBackgroundThread();
}
if (isCancelled) {
throw new CancellationException();
} else if (loadFailed) {
throw new ExecutionException(exception);
} else if (resultReceived) {
return resource;
}
if (timeoutMillis == null) {
waiter.waitForTimeout(this, 0);
} else if (timeoutMillis > 0) {
waiter.waitForTimeout(this, timeoutMillis);
}
if (Thread.interrupted()) {
throw new InterruptedException();
} else if (loadFailed) {
throw new GlideExecutionException(exception);
} else if (isCancelled) {
throw new CancellationException();
} else if (!resultReceived) {
throw new TimeoutException();
}
return resource;
}
RequestFutureTarget的onResourceReady内部没有做任何实现。而get()方法是个阻塞方法,如果图片还没有加载完成,get()的调用线程会被阻塞;同时get()内部也做了线程判断,如果不是在子线程,会抛异常。
Listener的功能和原理
listener()方法提供了一个功能:图片加载的状态,加载完成或者加载失败的结果。1
2
3
4boolean onLoadFailed(@Nullable GlideException e, Object model, Target<R> target,
boolean isFirstResource);
boolean onResourceReady(R resource, Object model, Target<R> target, DataSource dataSource,
boolean isFirstResource);
listener方法的参数RequestListener内部有两个方法
- onResourceReady 标识加载成功,以及图片资源resource,返回值标识是否处理了结果
- onLoadFailed 表示加载失败,以及失败的原因GlideException 。返回值标识是否处理了结果
listener方法的参数RequestListener,会在构建Request的时候,保存在request的属性requestListener。
在DecodeJob回调的流程中会在Request中调用onResourceReady或者onLoadFailed