Glide 加载图片,一般都是一行代码就搞定了,比如说在 Activity 中,简化一点的代码 Glide.with(this).load("http://p1.pstatp.com/large/166200019850062839d3").into(iv);这里面有三个方法,with() 方法上一章讲了一个例子,现在重点看下 load() 的方法,Glide.with(this) 返回的对象是 RequestManager,它的方法代码为
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
这里有两个方法,一个是 fromString(),另外一个是 load(string),我们先看第一个,这里面是逻辑的重点
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}
return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
loadGeneric(Class<T> modelClass) 中的形参是 String 类型的 Class,注意它的第一行代码返回的对象 ModelLoader,这是个接口
public interface ModelLoader<T, Y> {
DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}
由于加载图片由比较多的种类,比如从 drawable 文件、asset 文件、网络、手机SDK 等等,所以Glide中抽象了具体的功能, ModelLoader 泛型 <T, Y> ,表示传进去一个类型,返回的是另外一种,就像从网络获取图片,传进去的是 String, 返回的是流,然后转换为Drawable或Bitmap等等;我们看看 ModelLoader 是怎么创建的,看Glide中的 buildStreamModelLoader() 方法
public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
return buildModelLoader(modelClass, InputStream.class, context);
}
public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass, Context context) {
return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
}
private GenericLoaderFactory getLoaderFactory() {
return loaderFactory;
}
private final GenericLoaderFactory loaderFactory;
这里注意,最终调用的是 GenericLoaderFactory 的 buildModelLoader(modelClass, resourceClass) 方法,此时 modelClass 是 String.class,resourceClass 是 InputStream.class 类型; GenericLoaderFactory 这个类不复杂,就是一个缓存数据的类,它把数据进行了分类,装到了 Map 集合中,由于 Glide 是单例全局的,所以在Glide中的 loaderFactory 对象,也是全局的,我把GenericLoaderFactory的代码简化下
public class GenericLoaderFactory {
private final Map<Class, Map<Class, ModelLoaderFactory>> modelClassToResourceFactories = new HashMap();
private final Map<Class, Map<Class, ModelLoader>> cachedModelLoaders = new HashMap();
public synchronized <T, Y> ModelLoaderFactory<T, Y> register(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
...
Map<Class, ModelLoaderFactory> resourceToFactories = modelClassToResourceFactories.get(modelClass);
if (resourceToFactories == null) {
resourceToFactories = new HashMap<>();
modelClassToResourceFactories.put(modelClass, resourceToFactories);
}
ModelLoaderFactory previous = resourceToFactories.put(resourceClass, factory);
...
return previous;
}
public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass, Context context) {
return buildModelLoader(modelClass, resourceClass);
}
public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass) {
// 从缓存中取,如果有,直接返回
...
final ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass);
if (factory != null) {
result = factory.build(context, this);
cacheModelLoader(modelClass, resourceClass, result);
} else {
cacheNullLoader(modelClass, resourceClass);
}
return result;
}
// 缓存数据,ModelLoader
private <T, Y> void cacheModelLoader(Class<T> modelClass, Class<Y> resourceClass, ModelLoader<T, Y> modelLoader) {
Map<Class, ModelLoader> resourceToLoaders = cachedModelLoaders.get(modelClass);
if (resourceToLoaders == null) {
resourceToLoaders = new HashMap();
cachedModelLoaders.put(modelClass, resourceToLoaders);
}
resourceToLoaders.put(resourceClass, modelLoader);
}
private <T, Y> ModelLoaderFactory<T, Y> getFactory(Class<T> modelClass, Class<Y> resourceClass) {
Map<Class, ModelLoaderFactory> resourceToFactories = modelClassToResourceFactories.get(modelClass);
ModelLoaderFactory result = null;
if (resourceToFactories != null) {
result = resourceToFactories.get(resourceClass);
}
if (result == null) {
for (Class<? super T> registeredModelClass : modelClassToResourceFactories.keySet()) {
if (registeredModelClass.isAssignableFrom(modelClass)) {
Map<Class, ModelLoaderFactory> currentResourceToFactories = modelClassToResourceFactories.get(registeredModelClass);
if (currentResourceToFactories != null) {
result = currentResourceToFactories.get(resourceClass);
if (result != null) {
break;
}
}
}
}
}
return result;
}
}
看它里面的 buildModelLoader() 方法,其实注意两行就行了, getFactory(modelClass, resourceClass) 和 factory.build(context, this), getFactory() 是获取 ModelLoader的工厂类,也是先从集合中获取,如果为null,则会找 modelClass 的父类对应的 ModelLoaderFactory,那么 modelClassToResourceFactories 集合中的数据是怎么来的呢? Glide 的构造方法中会先添加一些封装好的数据
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
...
register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
register(File.class, InputStream.class, new StreamFileLoader.Factory());
register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(int.class, InputStream.class, new StreamResourceLoader.Factory());
register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
register(String.class, InputStream.class, new StreamStringLoader.Factory());
register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
...
}
public <T, Y> void register(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
ModelLoaderFactory<T, Y> removed = loaderFactory.register(modelClass, resourceClass, factory);
if (removed != null) {
removed.teardown();
}
}
Glide 默认已经提供了一系列的工厂类。ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass) 方法中,根据两个形参类型,可以确定 factory 对应 register(String.class, InputStream.class, new StreamStringLoader.Factory()) 中的 StreamStringLoader.Factory(),然后亮点来了,result = factory.build(context, this) 这行代码比较有意思,里面会形成一条串串,看看 StreamStringLoader.Factory() 的代码
public class StreamStringLoader extends StringLoader<InputStream> implements StreamModelLoader<String> {
public static class Factory implements ModelLoaderFactory<String, InputStream> {
@Override
public ModelLoader<String, InputStream> build(Context context, GenericLoaderFactory factories) {
return new StreamStringLoader(factories.buildModelLoader(Uri.class, InputStream.class));
}
...
}
...
public StreamStringLoader(ModelLoader<Uri, InputStream> uriLoader) {
super(uriLoader);
}
}
到这里,看似是结束了,build() 返回的是 StreamStringLoader 对象;实际上网络加载图片时,会调用 StreamStringLoader 的 getResourceFetcher() 方法,在它的父类中,为什么会调用,下篇文章再分析,这里先记住这一点,看它父类的方法
public class StringLoader<T> implements ModelLoader<String, T> {
private final ModelLoader<Uri, T> uriLoader;
public StringLoader(ModelLoader<Uri, T> uriLoader) {
this.uriLoader = uriLoader;
}
@Override
public DataFetcher<T> getResourceFetcher(String model, int width, int height) {
...
return uriLoader.getResourceFetcher(uri, width, height);
}
...
}
这里调用的是 uriLoader 的 getResourceFetcher(uri, width, height) 方法,uriLoader 是由 StreamStringLoader 传进来的,是工厂类 build()方法中的 factories.buildModelLoader(Uri.class, InputStream.class) 返回的对象,看到这,又返回到了 GenericLoaderFactory 类中,再次 getFactory(modelClass, resourceClass) 和 factory.build(context, this) 的逻辑,这次两个形参类型是 Uri.class、InputStream.class,我们找找 Glide 构造方法中对应的工厂类 StreamUriLoader.Factory
public class StreamUriLoader extends UriLoader<InputStream> implements StreamModelLoader<Uri> {
public static class Factory implements ModelLoaderFactory<Uri, InputStream> {
@Override
public ModelLoader<Uri, InputStream> build(Context context, GenericLoaderFactory factories) {
return new StreamUriLoader(context, factories.buildModelLoader(GlideUrl.class, InputStream.class));
}
...
}
public StreamUriLoader(Context context, ModelLoader<GlideUrl, InputStream> urlLoader) {
super(context, urlLoader);
}
...
}
StringLoader 中的 uriLoader.getResourceFetcher(uri, width, height) 对应的就是 StreamUriLoader 的 getResourceFetcher(uri, width, height) 方法,在它的父类中
public abstract class UriLoader<T> implements ModelLoader<Uri, T> {
private final Context context;
private final ModelLoader<GlideUrl, T> urlLoader;
public UriLoader(Context context, ModelLoader<GlideUrl, T> urlLoader) {
this.context = context;
this.urlLoader = urlLoader;
}
@Override
public final DataFetcher<T> getResourceFetcher(Uri model, int width, int height) {
final String scheme = model.getScheme();
DataFetcher<T> result = null;
if (isLocalUri(scheme)) {
...
} else if (urlLoader != null && ("http".equals(scheme) || "https".equals(scheme))) {
result = urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height);
}
return result;
}
...
}
看到这,仍然是调用了 urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height) 这个方法,那么 urlLoader 是什么?仍然是 StreamUriLoader 传进来的,对象是 factories.buildModelLoader(GlideUrl.class, InputStream.class),好吧,继续去 Glide 的构造方法中对应的值,发现是 HttpUrlGlideUrlLoader.Factory ,看看代码
public class HttpUrlGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private final ModelCache<GlideUrl, GlideUrl> modelCache;
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<GlideUrl, GlideUrl>(500);
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new HttpUrlGlideUrlLoader(modelCache);
}
...
}
public HttpUrlGlideUrlLoader() {
this(null);
}
public HttpUrlGlideUrlLoader(ModelCache<GlideUrl, GlideUrl> modelCache) {
this.modelCache = modelCache;
}
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
return new HttpUrlFetcher(url);
}
}
这次是返回了 HttpUrlGlideUrlLoader 对象,里面没有嵌套了,urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height) 中的 urlLoader 是 HttpUrlGlideUrlLoader 类型的,它的 getResourceFetcher() 方法,是获取 Uri 的 toString() 值,然后封装到 GlideUrl 对象中,作为参数传入,我们看下 HttpUrlGlideUrlLoader 中的 getResourceFetcher(GlideUrl model, int width, int height),发返回了个 HttpUrlFetcher 对象,看下代码
public class HttpUrlFetcher implements DataFetcher<InputStream> {
private static final String TAG = "HttpUrlFetcher";
private static final int MAXIMUM_REDIRECTS = 5;
private static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory();
private final GlideUrl glideUrl;
private final HttpUrlConnectionFactory connectionFactory;
private HttpURLConnection urlConnection;
private InputStream stream;
private volatile boolean isCancelled;
public HttpUrlFetcher(GlideUrl glideUrl) {
this(glideUrl, DEFAULT_CONNECTION_FACTORY);
}
HttpUrlFetcher(GlideUrl glideUrl, HttpUrlConnectionFactory connectionFactory) {
this.glideUrl = glideUrl;
this.connectionFactory = connectionFactory;
}
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 , null , glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
throws IOException {
...
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(2500);
urlConnection.setReadTimeout(2500);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
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 IOException("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 IOException("Unable to retrieve response code from HttpUrlConnection.");
}
throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
}
}
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 来请求图片url,然后以IO流的形式接受;也就是说,从 StreamStringLoader 到 HttpUrlGlideUrlLoader,最终是用的是 HttpUrlFetcher,而源头,则是 RequestManager 中的 loadGeneric(Class<T> modelClass) 方法, Glide.buildStreamModelLoader(modelClass, context) 代码返回的对象 streamModelLoader 是 StreamStringLoader 类型,到这一步还没有执行上面分析的一串,它是在 into() 方法中执行的,后面再分析。
加载网络图片,传入 String 类型,最后会被转成 GlideUrl 类型,然后使用 HttpUrlFetcher 来请求图片,如果我们想用自定义的网络怎么办?只要想办法把 Glide 构造中 register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory()) 中 GlideUrl 和 InputStream 对应的 HttpUrlGlideUrlLoader.Factory 替换掉即可,这样,通过 GlideUrl.class 和 InputStream.class 找工厂类就找到自定的,这时候返回自己的网络加载图片类就可以了,那么在哪里替换呢?Glide 是单例,看看它的方法
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}
ManifestParser 是解析配置清单中 <application /> 节点下面的 <meta-data /> 数据的,并且指定了 android:value="GlideModule",根据value获取到key的配置,然后通过反射创建一个对象,这个对象必须实现 GlideModule 接口,glide = builder.createGlide() 创建了 Glide 之后,会遍历 modules 集合,把 glide 作为参数穿进去,调用 registerComponents() 方法,我们可以在这个方法中重新 register() 自定义的网络。 以 okhttp 为例,代码如下,比较粗糙
<application>
<meta-data
android:name="com.test.cn.OkHttpGlideModule"
android:value="GlideModule"/>
</application>
public class OkHttpGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
@Override
public void registerComponents(Context context, Glide glide) {
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(selfOkHttpClient)); // selfOkHttpClient 是自己定义的Okhttp
}
}
public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private static volatile OkHttpClient defClient;
private OkHttpClient client;
private static OkHttpClient getClient() {
if (defClient == null) {
synchronized (Factory.class) {
if (defClient == null) {
defClient = new OkHttpClient();
}
}
}
return defClient;
}
public Factory() {
this(getClient());
}
public Factory(OkHttpClient client) {
this.client = client;
}
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new OkHttpUrlLoader(client);
}
@Override
public void teardown() {
}
}
private final OkHttpClient client;
public OkHttpUrlLoader(OkHttpClient client) {
this.client = client;
}
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
return new OkHttpStreamFetcher(client, model);
}
}
public class OkHttpStreamFetcher implements DataFetcher<InputStream> {
private final OkHttpClient client;
private final GlideUrl url;
private InputStream stream;
private ResponseBody responseBody;
public OkHttpStreamFetcher(OkHttpClient client, GlideUrl url) {
this.client = client;
this.url = url;
}
@Override
public InputStream loadData(Priority priority) throws Exception {
Request.Builder requestBuilder = new Request.Builder()
.url(url.toStringUrl());
for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();
Response response = client.newCall(request).execute();
responseBody = response.body();
long contentLength = responseBody.contentLength();
stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
return stream;
}
@Override
public void cleanup() {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignored
}
}
if (responseBody != null) {
responseBody.close();
}
}
@Override
public String getId() {
return url.getCacheKey();
}
@Override
public void cancel() {
}
}
其实关于Okhttp获取网络IO流,有现成三方库,在项目gradle中直接配置 implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0@aar' 即可,1.5.0@aar 是版本号,可以根据自己的项目调整。