SourceGenerator
在上文 Glide 源码解析之 ResourceCacheGenerator 我们分析了从磁盘获取资源,而 SourceGenerator 的任务则是从来源地获取资源,比如说传入的是 url ,则是从网络获取,这里分析的是从网络获取的情况。
在 startNext() 中,首先会判断 dataToCache 是否为 null ,这个是获取到资源后才赋值的,所以一开始是 null 。接着判断 sourceCacheGenerator 是否为 null ,这个是当 cacheData() 的时候才赋值的,所以也是 null 。
然后会进入循环中,这里的 DataFetcher 实际为 HttpUrlFetcher ,资源的提取过程就是由它执行。 HttpUrlFetcher 的具体的获取过程可以看下 Glide 源码解析之 DecodeHelper
class SourceGenerator implements DataFetcherGenerator,
DataFetcher.DataCallback<Object>,
DataFetcherGenerator.FetcherReadyCallback {
private final DecodeHelper<?> helper;
private final FetcherReadyCallback cb;
private int loadDataListIndex;
private DataCacheGenerator sourceCacheGenerator;
private Object dataToCache;
@Override
public boolean startNext() {
//第一次执行的时候是等于 null 的
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
//第一次执行的时候是等于 null 的
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;
//此处为实际发起请求的部分
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
}
资源的提取
使用HttpURLConnection来进行网络连接,获取输入流。
最后就会把结果回调给 callback 了,这个 callback 是由 SourceGenerator 来实现的,也就是加载完后会通知到 SourceGenerator 。
//HttpUrlFetcher
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
callback.onLoadFailed(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 {
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); // (HttpURLConnection) url.openConnection();
//添加Header
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);
// 关闭网络连接的重定向,如果需要重定向会递归调用 loadDataWithRedirects() 自己处理
urlConnection.setInstanceFollowRedirects(false);
urlConnection.connect();
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);
// 关闭输入流和连接,重定向时会重新连接
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);
}
}
//获取网络连接返回的输入流
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength); //new ContentLengthInputStream(other, contentLength);
} else {
stream = urlConnection.getInputStream();
}
return stream;
}
@Override
public void onDataReady(Object data) {
//默认的 DiskCacheStrategy 为 DiskCacheStrategy.AUTOMATIC ,在 BaseRequestOptions中定义
//所以默认是会缓存远程资源的
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
//把资源赋值给dataToCache
dataToCache = data;
// 可能是在其他线程回调的,在执行接下来的任务时先切回到Glide的线程
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
资源获取完成
资源获取完成后会回调到 SourceGenerator 的 onDataReady(),如果是可以进行磁盘缓存的话(默认可以)则会回调给 DecodeJob 的 reschedule(),接着交给 EngineJob 使用线程池来重新执行 DecodeJob 。最后又会执行到 SourceGenerator 的startNext()方法。
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data; //赋值给dataToCache
//可能会在其他线程回回调,在执行其他东西之前先回调到 Glide 的线程
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
//DecodeJob
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
//EngineJob
@Override
public void reschedule(DecodeJob<?> job) {
getActiveSourceExecutor().execute(job);
}
//DecodeJob
@Override
public void run() {
runWrapped();
}
private void runWrapped() {
switch (runReason) {
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
}
}
private void runGenerators() {
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
}
}
缓存资源
由于在上面的 onDataReady()后给 dataToCache 赋值了,所以这里会进入到 cacheData() 里面。在里面将资源写进磁盘缓存,然后给 sourceCacheGenerator 赋值为 DataCacheGenerator ,接着调用它的 startNext() 去磁盘获取资源。
为什么 SourceGenerator 获取到资源后不自己缓存后就返回还要调用 DataCacheGenerator 去再获取一遍呢?我想主要是为了使各个类遵循单一职责,DataCacheGenerator是负责从磁盘获取资源的则交给它去完成。在 onDataReady() 中,当不需要缓存的时候,SourceGenerator 也是直接回调给 DecodeJob 的。
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
}
/**
* 将数据缓存至磁盘缓存
*/
private void cacheData(Object dataToCache) {
try {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
helper.getDiskCache().put(originalKey, writer); //写进磁盘缓存
} finally {
loadData.fetcher.cleanup();
}
//在这里初始化sourceCacheGenerator
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}