前言
glide是谷歌推荐的Android图片加载框架,其优秀的缓存策略、Activity的生命周期的继承、GIF图片的支持等都是为人所称道的地方。下面是用glide加载一张图片的调用。
private void loadImage() {
Glide.with(this)
.load("http://pic2.orsoon.com/2017/0118/20170118011032176.jpg")
.into(ivTest);
}
那么,该框架是如何实际运作的呢,我会通过“Glide之旅”系列博客尽可能详细地将我的心得记录下来。“Glide之旅”系列文章汇总:
Glide之旅 —— Key
概述
Key(com.bumptech.glide.load.Key),顾名思义,是用来作为图片的唯一标识,那么具体又是怎样的呢?
已知实现类
Key是一个接口,其所有实现类有
com.bumptech.glide.load.resource.bitmap.BitmapDrawableTransformation
com.bumptech.glide.load.engine.prefill.BitmapPreFillRunner.UniqueKey
com.bumptech.glide.load.resource.bitmap.BitmapTransformation
com.bumptech.glide.load.resource.bitmap.CenterCrop
com.bumptech.glide.load.resource.bitmap.CenterInside
com.bumptech.glide.load.resource.bitmap.CircleCrop
com.bumptech.glide.load.engine.DataCacheKey
com.bumptech.glide.signature.EmptySignature
com.bumptech.glide.load.engine.EngineKey
com.bumptech.glide.load.resource.bitmap.FitCenter
com.bumptech.glide.load.resource.gif.GifDrawableTransformation
com.bumptech.glide.load.resource.gif.GifFrameLoader.FrameSignature
com.bumptech.glide.load.model.GlideUrl
com.bumptech.glide.signature.MediaStoreSignature
com.bumptech.glide.load.MultiTransformation<T>
com.bumptech.glide.signature.ObjectKey
com.bumptech.glide.load.Options
com.bumptech.glide.load.engine.ResourceCacheKey
com.bumptech.glide.load.resource.bitmap.RoundedCorners
com.bumptech.glide.load.resource.UnitTransformation<T>
GlideUrl
当调用前言中的loadImage()
进行图片加载的时候,结合我的Glide之旅 —— DecodeJob一文中的源码流程可知,用SourceGenerator
解码远程图片的时候,首先会执行到com.bumptech.glide.load.model.stream.HttpUriLoader.Factory#build(MultiModelLoaderFactory)
,先来看下源码
package com.bumptech.glide.load.model.stream;
...
public class HttpUriLoader implements ModelLoader<Uri, InputStream> {
...
public static class Factory implements ModelLoaderFactory<Uri, InputStream> {
@Override
public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new HttpUriLoader(multiFactory.build(GlideUrl.class, InputStream.class));
}
@Override
public void teardown() {
// Do nothing.
}
}
}
那么显然,根据Glide之旅 —— Registry中介绍的获取注册项方法可知,需要在ModelLoaderRegistry
数据集合中找出Entry
第一个传参为GlideUrl.class
,第二个传参为InputStream.class
的数据项,可知符合条件的有
23. new MultiModelLoaderFactory.Entry(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
那么,此时再来看下com.bumptech.glide.load.model.stream.HttpGlideUrlLoader
的源码
package com.bumptech.glide.load.model.stream;
...
public class HttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
public static final Option<Integer> TIMEOUT = Option.memory(
"com.bumptech.glide.load.model.stream.HttpGlideUrlLoader.Timeout", 2500);
@Nullable
private final ModelCache<GlideUrl, GlideUrl> modelCache;
public HttpGlideUrlLoader() {
this(null);
}
public HttpGlideUrlLoader(ModelCache<GlideUrl, GlideUrl> modelCache) {
this.modelCache = modelCache;
}
@Override
public LoadData<InputStream> buildLoadData(GlideUrl model, int width, int height,
Options options) {
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
@Override
public boolean handles(GlideUrl model) {
return true;
}
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<>(500);
@Override
public ModelLoader<GlideUrl, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new HttpGlideUrlLoader(modelCache);
}
@Override
public void teardown() {
// Do nothing.
}
}
}
可以看到,buildLoadData(GlideUrl, int, int, Options)
的返回值为new LoadData<>(url, new HttpUrlFetcher(url, timeout))
,其中,url
就是一个用访问的图片地址转换成的GlideUrl
对象
package com.bumptech.glide.load.model;
...
public interface ModelLoader<Model, Data> {
class LoadData<Data> {
public final Key sourceKey;
public final List<Key> alternateKeys;
public final DataFetcher<Data> fetcher;
public LoadData(Key sourceKey, DataFetcher<Data> fetcher) {
this(sourceKey, Collections.<Key>emptyList(), fetcher);
}
public LoadData(Key sourceKey, List<Key> alternateKeys, DataFetcher<Data> fetcher) {
this.sourceKey = Preconditions.checkNotNull(sourceKey);
this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
this.fetcher = Preconditions.checkNotNull(fetcher);
}
}
...
}
也就是说,该LoadData
对象的sourceKey
为一个GlidUrl
对象,而该GlidUrl
对象是有图片来源地址转换而来的,看下GlideUrl
源码
package com.bumptech.glide.load.model;
...
public class GlideUrl implements Key {
private static final String ALLOWED_URI_CHARS = "@#&=*+-_.,:!?()/~'%";
private final Headers headers;
@Nullable
private final URL url;
@Nullable
private final String stringUrl;
@Nullable
private String safeStringUrl;
@Nullable
private URL safeUrl;
@Nullable
private volatile byte[] cacheKeyBytes;
private int hashCode;
public GlideUrl(URL url) {
this(url, Headers.DEFAULT);
}
public GlideUrl(String url) {
this(url, Headers.DEFAULT);
}
public GlideUrl(URL url, Headers headers) {
this.url = Preconditions.checkNotNull(url);
stringUrl = null;
this.headers = Preconditions.checkNotNull(headers);
}
public GlideUrl(String url, Headers headers) {
this.url = null;
this.stringUrl = Preconditions.checkNotEmpty(url);
this.headers = Preconditions.checkNotNull(headers);
}
public URL toURL() throws MalformedURLException {
return getSafeUrl();
}
private URL getSafeUrl() throws MalformedURLException {
if (safeUrl == null) {
safeUrl = new URL(getSafeStringUrl());
}
return safeUrl;
}
public String toStringUrl() {
return getSafeStringUrl();
}
private String getSafeStringUrl() {
if (TextUtils.isEmpty(safeStringUrl)) {
String unsafeStringUrl = stringUrl;
if (TextUtils.isEmpty(unsafeStringUrl)) {
unsafeStringUrl = url.toString();
}
safeStringUrl = Uri.encode(unsafeStringUrl, ALLOWED_URI_CHARS);
}
return safeStringUrl;
}
public Map<String, String> getHeaders() {
return headers.getHeaders();
}
public String getCacheKey() {
return stringUrl != null ? stringUrl : url.toString();
}
@Override
public String toString() {
return getCacheKey();
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
messageDigest.update(getCacheKeyBytes());
}
private byte[] getCacheKeyBytes() {
if (cacheKeyBytes == null) {
cacheKeyBytes = getCacheKey().getBytes(CHARSET);
}
return cacheKeyBytes;
}
@Override
public boolean equals(Object o) {
if (o instanceof GlideUrl) {
GlideUrl other = (GlideUrl) o;
return getCacheKey().equals(other.getCacheKey())
&& headers.equals(other.headers);
}
return false;
}
@Override
public int hashCode() {
if (hashCode == 0) {
hashCode = getCacheKey().hashCode();
hashCode = 31 * hashCode + headers.hashCode();
}
return hashCode;
}
}