介绍
上一篇介绍了加载资源的流程,主要讲解了加载资源的主流程,本篇主要讲解从Model到Data是如何加载的?
ModelLoad
整个过程都是在DataFetcherGenerator的startNext()函数中执行的,我们分析主要的SourceGenerator
@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()) {
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;
}
private boolean hasNextModelLoader() {
return loadDataListIndex < helper.getLoadData().size();
}
DecodeHelper#getLoadData()
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model); //1
//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);//2 构建LoadData
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
第一步通过根据model(图片的网络地址)从Registry找到所有合适的ModelLoader
@NonNull
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
...
return result;
}
Registry 和其他的子模块的注册器 是一个外观模式,将所有的子模块都封装到这里边,委托modelLoaderRegistry查找
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
@NonNull
public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));//根据Model查询所有tModelLoaders
int size = modelLoaders.size();
boolean isEmpty = true;
List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0; i < size; i++) {
ModelLoader<A, ?> loader = modelLoaders.get(i);
if (loader.handles(model)) {
if (isEmpty) {
filteredLoaders = new ArrayList<>(size - i);
isEmpty = false;
}
filteredLoaders.add(loader);
}
}
return filteredLoaders;
}
@NonNull
private synchronized <A> List<ModelLoader<A, ?>> getModelLoadersForClass(
@NonNull Class<A> modelClass) {
List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
if (loaders == null) {
loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
cache.put(modelClass, loaders);
}
return loaders;
}
MultiModelLoaderFactory#build
@NonNull
synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
try {
List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
for (Entry<?, ?> entry : entries) {
if (alreadyUsedEntries.contains(entry)) {
continue;
}
if (entry.handles(modelClass)) {//1
alreadyUsedEntries.add(entry);
loaders.add(this.<Model, Object>build(entry));//build
alreadyUsedEntries.remove(entry);
}
}
return loaders;
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}
private <Model, Data> ModelLoader<Model, Data> build(@NonNull Entry<?, ?> entry) {
return (ModelLoader<Model, Data>) Preconditions.checkNotNull(entry.factory.build(this));
}
注释1处会过滤注册表中所有能够处理当前modelClass(String.class)的entry,然后调用factory的build方法构建一个ModelLoader对象。注册表的内容在Glide的构造方法中实现的。
Glide(...){
registry
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(
String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())+
...
}
过滤处符合条件的就是new DataUrlLoader.StreamFactory<String>(), new StringLoader.StreamFactory(), new StringLoader.FileDescriptorFactory() new StringLoader.AssetFileDescriptorFactory() 四个对象我们看一下具体的内容
public class StringLoader<Data> implements ModelLoader<String, Data> {
/** 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.
}
}
/** Factory for loading {@link ParcelFileDescriptor}s from Strings. */
public static class FileDescriptorFactory
implements ModelLoaderFactory<String, ParcelFileDescriptor> {
@NonNull
@Override
public ModelLoader<String, ParcelFileDescriptor> build(
@NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, ParcelFileDescriptor.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
/** Loads {@link AssetFileDescriptor}s from Strings. */
public static final class AssetFileDescriptorFactory
implements ModelLoaderFactory<String, AssetFileDescriptor> {
@Override
public ModelLoader<String, AssetFileDescriptor> build(
@NonNull MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, AssetFileDescriptor.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
}
public final class DataUrlLoader<Model, Data> implements ModelLoader<Model, Data> {
public static final class StreamFactory<Model> implements ModelLoaderFactory<Model, InputStream> {
private final DataDecoder<InputStream> opener;
public StreamFactory() {
opener =
new DataDecoder<InputStream>() {
...
};
}
@NonNull
@Override
public ModelLoader<Model, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
return new DataUrlLoader<>(opener);
}
@Override
public void teardown() {
// Do nothing.
}
}
}
四个factory的build方法的到的是三个StringLoader对象和一个DataUrlLoader对象。回到getModelLoaders方法内filteredLoaders集合根据loader.handles(model)将不符合条件的剔除掉,其中DataUrlLoader接受处理的string,是"data:image"开始的,我们传入的是网络图片,所以整个不符合条件,剩下三个StringLoader对象。
DecodeHelper#getLoadData()方法的注释2处,根据model构建LoadData对象。
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)) {//1
return null;
}
return uriLoader.buildLoadData(uri, width, height, options);
}
@Nullable
private static Uri parseUri(String model) {
Uri uri;
if (TextUtils.isEmpty(model)) {
return null;
// See https://pmd.github.io/pmd-6.0.0/pmd_rules_java_performance.html#simplifystartswith
} else if (model.charAt(0) == '/') {
uri = toFileUri(model);
} else {
uri = Uri.parse(model);
String scheme = uri.getScheme();
if (scheme == null) {
uri = toFileUri(model);
}
}
return uri;
}
...
}
StringLoader中的buildLoadData方法又委托uriLoader处理,uriLoader属性是在构造方法内赋值的,是在fatcory 的build的方法中传入的。三个工厂的build方法分别是:multiFactory.build(Uri.class, InputStream.class);multiFactory.build(Uri.class, ParcelFileDescriptor.class); multiFactory.build(Uri.class, AssetFileDescriptor.class),我们根据Uri.class 和 Data.class从Glide的注册表中找到合适的ModelLoader.Glide 的构造方法中有如下:
registry
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())//1 data:image
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(
String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
.append(Uri.class, InputStream.class, new HttpUriLoader.Factory())//2
.append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))//3 android_asset
.append(
Uri.class,
ParcelFileDescriptor.class,
new AssetUriLoader.FileDescriptorFactory(context.getAssets()))//8
.append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))//4
.append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))//5
.append(Uri.class, InputStream.class, new UriLoader.StreamFactory(contentResolver))//6 file content
.append(
Uri.class,
ParcelFileDescriptor.class,
new UriLoader.FileDescriptorFactory(contentResolver))//9
.append(
Uri.class,
AssetFileDescriptor.class,
new UriLoader.AssetFileDescriptorFactory(contentResolver))//10
.append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())//7
由于篇幅有限,StringLoad中ModelLoaderFactory的multiFactory.build(Uri.class, XXX.class)的流程就不贴出来了,我就在这里直接说结果:
第一个StringLoader根据multiFactory.build(Uri.class, InputStream.class);方法的参数找到的是上边注释1-7的工厂build出来的loader集合构成的MultiModelLoader对象,如下所示,其中modelLoaders集合就是上边注释1-7的loader
static class Factory {
@NonNull
public <Model, Data> MultiModelLoader<Model, Data> build(
@NonNull List<ModelLoader<Model, Data>> modelLoaders,
@NonNull Pool<List<Throwable>> throwableListPool) {
return new MultiModelLoader<>(modelLoaders, throwableListPool);
}
}
第二个是StringLoader 根据Uri.class, ParcelFileDescriptor.class 从注册表中找到的是注释8-9的Factory build的AssetUriLoader和UriLoader 集合构建的MultiModelLoader 对象,伪代码如下:
List<ModelLoader<Model, Data>> modelLoaders = new ArrayList<>(){
{
add(new AssetUriLoader());
add(new UriLoader ());
}
};
MultiModelLoader multiModelLoader = new MultiModelLoader(modelLoaders, throwableListPool);
StringLoader stringLoader2 = new StringLoader(multiModelLoader );
这里可以看出MultiModelLoader和其他的ModelLoader 组成了 组合设计模式。
第三个StringLoader 对象根据Uri.class, AssetFileDescriptor.class 从注册表中找到的是注释10 处的AssetFileDescriptorFactory build的UriLoader 对象。伪代码如下:
StringLoader stringLoader3= new StringLoader(new UriLoader ());
回到StringLoader#buildLoadData方法的注释1处,根据传入的String解析是协议是http类型的,上边三个StringLoader中的10个modelLoader只有UrlUriLoader和HttpUriLoader两个类可以处理http类型的。这两个类也是通过传入的其他ModelLoader处理的加载任务,通过他们内部类的工厂的build方法传入的loader,两个类都是传入的multiFactory.build(GlideUrl.class, InputStream.class),根据GlideUrl.class, InputStream.class从注册表中找到的是HttpGlideUrlLoader类。MultiModelLoader#buildLoadData
@Override
public LoadData<Data> buildLoadData(
@NonNull Model model, int width, int height, @NonNull Options options) {
Key sourceKey = null;
int size = modelLoaders.size();
List<DataFetcher<Data>> fetchers = new ArrayList<>(size);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0; i < size; i++) {
ModelLoader<Model, Data> modelLoader = modelLoaders.get(i);
if (modelLoader.handles(model)) {
LoadData<Data> loadData = modelLoader.buildLoadData(model, width, height, options);
if (loadData != null) {
sourceKey = loadData.sourceKey;
fetchers.add(loadData.fetcher);//1
}
}
}
return !fetchers.isEmpty() && sourceKey != null
? new LoadData<>(sourceKey, new MultiFetcher<>(fetchers, exceptionListPool))
: null;
}
上边注释1处的fetchers添加的是两个HttpGlideUrlLoader中buildLoadData构建出来的HttpUrlFetcher对象
回到开始的DecodeHelper#getLoadData()方法的伪代码是:
List<LoadData<?>> getLoadData() {
List<DataFetcher<Data>> fetchers = new ArrayList<>(2);
fetchers.add(new HttpUrlFetcher(new GlideUrl("图片的地址")));
fetchers.add(new HttpUrlFetcher(new GlideUrl("图片的地址")));
MultiFetcher multiFetcher = new MultiFetcher(fetchers, throwableListPool);
LoadData<?> current = new LoadData("图片的地址",multiFetcher);
loadData.add(current);
return loadData;
}
现在再看一下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()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {//getDataClass()=InputStream.class
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);//2
}
}
return started;
}
helper.hasLoadPath(loadData.fetcher.getDataClass())这个过程会根据 Data ,Resource和 TrancodeResource检查注册表是否有合适的LoadPath,后边分析解码和转码的时候也会用到,找个分析就放到后边的文章。接着会执行注释2的地方,将结果回调给当前的类
@Override
public void onDataReady(Object data) {
//
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
cb.reschedule();
} else {
cb.onDataFetcherReady(
loadData.sourceKey,
data,
loadData.fetcher,
loadData.fetcher.getDataSource(),
originalKey);
}
}
当设置使用缓存的时候,会将从model fetch到的data缓存到磁盘中,这里的缓存的数据就是原始数据,在下次加载资源的时候会用到,上一篇文章已经分析过了。所以这里分析不使用缓存的时候,将data回传到DecodeJob的onDataFetcherReady中。
到这里从Model加载成Data的过程已经全部分析完了。
那么多从Model加载到Data 的DataFetcher中,我们选择一个分析一下,我们加载url的string的时候,使用的是HttpUrlFetcher中的loadData方法。所以分一下HttpUrlFetcher#loadData
HttpUrlFetcher#loadData
@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) {
callback.onLoadFailed(e);
}
...
}
private InputStream loadDataWithRedirects(
URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {//最大重定向次数是5次
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
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {//添加header
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);//连接超时 2500
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
urlConnection.setInstanceFollowRedirects(false);
urlConnection.connect();
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {//1
return getStreamForSuccessfulRequest(urlConnection);//3
} else if (isHttpRedirect(statusCode)) {//2
String redirectUrlString = urlConnection.getHeaderField("Location");
...
URL redirectUrl = new URL(url, redirectUrlString);
...
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
}
...
}
loadData方法调用了 重定向加载方法loadDataWithRedirects,上边注释1处是加载成功返回结果,注释2处是响应码是3XX 重定向,从响应头中拿到重定向的地址进行下一次的请求。我们看一下注释3处
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
stream = urlConnection.getInputStream();
}
return stream;
}
没有ContentEncoding的时候,使用ContentLengthInputStream,有ContentEncoding的时候 使用InputStream。
至此整个流程已经分析完毕。
网友评论