Ahab's Studio.

Glide 源码分析 - 下载及预加载

字数统计: 832阅读时长: 4 min
2019/02/17 Share

下载的标准写法如下,也是官方示例写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@WorkerThread
private void downloadFile() {
FutureTarget<File> target = null;
try {
target = Glide.with(context)
.downloadOnly()
.load(imgUrl)
.submit();
final File cacheFile = target.get();
/*
* 默认会下载到磁盘缓存中,理论上不应对缓存文件进行编辑、删除
*/
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "download: ", e);
} finally {
// 这里要调用cancel方法取消等待操作并释放资源
if (target != null) {
target.cancel(true); // 若传true则允许中断操作
}
}
}

此方式要自行开子线程,你可能会觉得稍显麻烦,直接调用 listener 方法监听 onResourceReady 回调岂不是更简单?其实不是的,由于要拿到 FutureTarget 调用其 cancel 方法,若监听 onResourceReady 代码逻辑会更复杂。

对于 FutureTarget.get() 方法,并不是调用时才会去加载数据,调用 submit 方法后就已经开始去加载数据了,get 方法最终会调用到 RequestFutureTarget 的 doGet 方法如下:

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
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) {
long now = System.currentTimeMillis();
long deadline = now + timeoutMillis;
while (!isDone() && now < deadline) {
waiter.waitForTimeout(this, deadline - now);
now = System.currentTimeMillis();
}
}

if (Thread.interrupted()) {
throw new InterruptedException();
} else if (loadFailed) {
throw new ExecutionException(exception);
} else if (isCancelled) {
throw new CancellationException();
} else if (!resultReceived) {
throw new TimeoutException();
}
return resource;
}

可以看到 get 方法内部并没有加载数据的逻辑, RequestFutureTarget 内部通过锁实现了 get 方法的阻塞调用,当资源加载完毕后 onResourceReady 中会解除阻塞:

1
2
3
4
5
6
7
8
9
@Override
public synchronized boolean onResourceReady(
R resource, Object model, Target<R> target, DataSource dataSource, boolean isFirstResource) {
// We might get a null result.
resultReceived = true;
this.resource = resource;
waiter.notifyAll(this);
return false;
}

除了下载 File 类型以外,还可以指定下载类型,比如下载 Bitmap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@WorkerThread
private void downloadBitmap() {
RequestOptions DOWNLOAD_ONLY_OPTIONS = RequestOptions
.diskCacheStrategyOf(DiskCacheStrategy.DATA) //这边其实可以根据业务场景配置,如果是网络图片一般需要缓存
.priority(Priority.LOW) // 设置优先级
.skipMemoryCache(true);
FutureTarget<Bitmap> target = null;
try {
target = Glide.with(context)
.asBitmap()
.apply(DOWNLOAD_ONLY_OPTIONS)
.load(imgUrl)
.submit();
final Bitmap bitmap = target.get();

} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "download: ", e);
} finally {
// 这里要调用cancel方法取消等待操作并释放资源
if (target != null) {
target.cancel(true); // 若传true则允许中断操作
}
}
}

这里的 DOWNLOAD_ONLY_OPTIONS 配置其实就是 downloadOnly 方法应用的配置。

实现预加载十分简单:

1
Glide.with(context).load(imgUrl).preload();

关键代码位于 PreloadTarget 中:

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
public final class PreloadTarget<Z> extends SimpleTarget<Z> {
private static final int MESSAGE_CLEAR = 1;
private static final Handler HANDLER = new Handler(Looper.getMainLooper(), new Callback() {
@Override
public boolean handleMessage(Message message) {
if (message.what == MESSAGE_CLEAR) {
((PreloadTarget<?>) message.obj).clear();
return true;
}
return false;
}
});

private final RequestManager requestManager;

public static <Z> PreloadTarget<Z> obtain(RequestManager requestManager, int width, int height) {
return new PreloadTarget<>(requestManager, width, height);
}

private PreloadTarget(RequestManager requestManager, int width, int height) {
super(width, height);
this.requestManager = requestManager;
}

@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
HANDLER.obtainMessage(MESSAGE_CLEAR, this).sendToTarget();
}

@SuppressWarnings("WeakerAccess")
@Synthetic void clear() {
requestManager.clear(this);
}
}

相比于 RequestFutureTarget,PreloadTarget 里的逻辑就简单多了,可以看到加载资源结束后只是把此次请求释放掉了,不用像其他 Target 一样做额外的操作。

关注公众号,Get 更多知识点

原文作者:Ahab

原文链接:http://yhaowa.gitee.io/a7bea0ed/

发表日期:February 17th 2019, 4:21:41 pm

更新日期:April 7th 2020, 11:34:42 pm

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG