本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。
前言
Glide是纯Java写的Android端开源图片加载库,能够帮助我们下载、缓存、展示多种格式图片,也包括GIF格式;
昨天我们从源码里分析了,glide的缓存策略机制;
那今天我们就趁热打铁来分析一波加载流程;
一、glide常用的加载方法
1、加载图片到imageView
Glide.with(Context context).load(Strint url).into(ImageView imageView);
- 1.
2、各种形式的图片加载到ImageView
// 加载本地图片
File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);
// 加载应用资源
int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);
// 加载二进制流
byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);
// 加载Uri对象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
3、加载带有占位图
Glide.with(this).load(url).placeholder(R.drawable.loading).into(imageView);
- 1.
占位图目的为在目的图片还未加载出来的时候,提前展示给用户的一张图片;
4、加载失败 放置占位符
Glide.with(this).load(url).placeholder(R.drawable.loading).error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)//关闭Glide的硬盘缓存机制
.into(imageView);
//DiskCacheStrategy.NONE:表示不缓存任何内容。
//DiskCacheStrategy.SOURCE:表示只缓存原始图片。
//DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。
//DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
5、加载指定格式的图片--指定为静止图片
Glide.with(this)
.load(url)
.asBitmap()//只加载静态图片,如果是git图片则只加载第一帧。
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
6、加载动态图片
Glide.with(this)
.load(url)
.asGif()//加载动态图片,若现有图片为非gif图片,则直接加载错误占位图。
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
7、加载指定大小的图片
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(100, 100)//指定图片大小
.into(imageView);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
8、关闭框架的内存缓存机制
Glide.with(this)
.load(url)
.skipMemoryCache(true) //传入参数为false时,则关闭内存缓存。
.into(imageView);
- 1.
- 2.
- 3.
- 4.
9、关闭硬盘的缓存
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE) //关闭硬盘缓存操作
.into(imageView);
//其他参数表示:
//DiskCacheStrategy.NONE:表示不缓存任何内容。
//DiskCacheStrategy.SOURCE:表示只缓存原始图片。
//DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。
//DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
10、当引用的 url 存在 token 时解决方法
public class MyGlideUrl extends GlideUrl {
private String mUrl;
public MyGlideUrl(String url) {
super(url);
mUrl = url;
}
@Override
public String getCacheKey() {
return mUrl.replace(findTokenParam(), "");
}
private String findTokenParam() {
String tokenParam = "";
int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token=");
if (tokenKeyIndex != -1) {
int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1);
if (nextAndIndex != -1) {
tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1);
} else {
tokenParam = mUrl.substring(tokenKeyIndex);
}
}
return tokenParam;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
然后加载图片的方式为:
Glide.with(this)
.load(new MyGlideUrl(url))
.into(imageView);
- 1.
- 2.
- 3.
11、利用Glide将图片加载到不同控件或加载成不同使用方式
(1)、拿到图片实例
//1、通过自己构造 target 可以获取到图片实例
SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
imageView.setImageDrawable(resource);
}
};
//2、将图片实例记载到指定的imageview上,也可以做其他的事情
public void loadImage(View view) {
String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
Glide.with(this)
.load(url)
.into(simpleTarget);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
(2)、将图片加载到任何位置
/*
*将图片加载为控件背景
*/
public class MyLayout extends LinearLayout {
private ViewTarget<MyLayout, GlideDrawable> viewTarget;
public MyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) {
MyLayout myLayout = getView();
myLayout.setImageAsBackground(resource);
}
};
}
public ViewTarget<MyLayout, GlideDrawable> getTarget() {
return viewTarget;
}
public void setImageAsBackground(GlideDrawable resource) {
setBackground(resource);
}
}
//引用图片到指定控件作为背景
public class MainActivity extends AppCompatActivity {
MyLayout myLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myLayout = (MyLayout) findViewById(R.id.background);
}
public void loadImage(View view) {
String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg";
Glide.with(this)
.load(url)
.into(myLayout.getTarget());
}
}
- 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.
- 37.
- 38.
12、Glide 实现预加载
//a、预加载代码
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.preload();
//preload() 有两种重载
// 1、带有参数的重载,参数作用是设置预加载的图片大小;
//2、不带参数的表示加载的图片为原始尺寸;
//b、使用预加载的图片
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
二、Glide加载流程详解
1、with(context)
// Glide.java
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getContext()).get(fragment);
}
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
该函数创建了Glide实例并初始化了一些基本参数,然后创建了一个RequestManager对象并返回。总共有5个场景,这里就先选取参数为Context类型情形进行分析。
// Glide.java
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
- 1.
- 2.
- 3.
- 4.
可以看到该函数首先调用了getRetriever(context)获取到了RequestManagerRetriever对象。在创建该对象之前首先通过Glide.java中的get方法获得了Glide实例(Glide是一个单例),同时读取AppGlideModule和AndroidManifest.xml的配置。
// Glide.java
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Glide.get(context)获取Glide实例
return Glide.get(context).getRequestManagerRetriever();
}
public static Glide get(@NonNull Context context) {
if (glide == null) {
// 加载AppGlideModule
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
// 加载Mainfest配置、注册模块回调
// 这一步执行了 Glide.build()方法构造Glide实例。build方法下面会讲到
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
获取到Glide实例后,紧接着调用getRequestManagerRetriever方法返回了上一步已经初始化好的RequestManagerRetriever对象。
// Glide.java
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
- 1.
- 2.
- 3.
- 4.
接着再看一看RequestManagerRetriever是如何被初始化的,以及初始化过程中都干了哪些事。首先贴源码看看Glide.build方法内部具体实现(该方法在上述checkAndInitializeGlide()函数中被调用):
// GlideBuilder.java
Glide build(@NonNull Context context) {
// 分配线程池、配置缓存策略
sourceExecutor = GlideExecutor.newSourceExecutor();
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
animationExecutor = GlideExecutor.newAnimationExecutor();
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
// 监听网络变化
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
// engine是负责执行加载任务的
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
animationExecutor,
isActiveResourceRetentionAllowed);
}
if (defaultRequestListeners == null) {
defaultRequestListeners = Collections.emptyList();
} else {
defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled,
isImageDecoderEnabledForBitmaps);
}
- 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.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 执行Glide.get()方法时就已经分配好了资源加载、缓存线程池、配置好了缓存策略,这里的engine专门负责加载、解码资源,ConnectivityMonitor注册了网络状态监听器,当网络断开时暂停请求网络资源,重连后继续请求资源;
- RequestManagerRetriever是原来是通过RequestManagerFactory工厂类构造的。进入到RequestManagerFactory.java类中,可以看到get方法获取到了相应的RequestManager对象;
- 从这里我们可以发现,无论哪种情况,当App进入后台后会导致页面不可见,此时RequestManager绑定到了ApplicationContext,与App的生命周期一致,因此在RequestManager.java类中也实现了生命周期相关的回调函数;
// RequestManagerRetriever.java
// get有好几个重载方法,这里仅选取context参数进行分析
public RequestManager get(@NonNull Context context) {
if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
执行完Glide.with(context)后我们拿到了一个对应的RequestManager对象,接下来就执行下一个任务load(url);
2、load(url)
拿到了RequestManager,紧接着调用load方法开始执行下一步操作,同样先看看load方法的实现
// RequestManager.java
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
public RequestBuilder<Drawable> load(@Nullable File file) {
return asDrawable().load(file);
}
public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return asDrawable().load(resourceId);
}
public RequestBuilder<Drawable> load(@Nullable URL url) {
return asDrawable().load(url);
}
public RequestBuilder<Drawable> load(@Nullable byte[] model) {
return asDrawable().load(model);
}
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
- 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.
- load()同样有多个重载函数,传入的参数可以是图片对象Bitmap、Drawable、本地资源Uri、在线资源路径Url、文件对象File、assets资源的id,这里我们只看参数为Url的情形;
- asDrawable().load(url)返回了一个RequestBuilder对象,首先看看asDrawable方法干了什么;
// RequestManager.java
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
asDrawable方法创建了RequestBuilder对象,然后调用RequestBuilder.java中的load方法;
// RequestBuilder.java
// 传入的String类型的url将会被作为缓存的key
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
// 这里返回了自身
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
load函数主要工作就是根据传入的资源类型,构造了一个相应的RequestBuilder对象。至此一切准备工作准备就绪,接下来就是最为重要的一步了-加载、展示文件,让我们来着看into(view)方法如何完成这些任务;
3、into(view)
拿到的是对应类型RequestBuilder实例,那么就看看该类里into方法的具体实现。同样into方法有into(@NonNull Y target)和into(@NonNull ImageView )两个重载函数(这两个函数最终都会走到同一个函数中),由于调用into方法时我们传入的参数是ImageView类型的;
// RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
BaseRequestOptions<?> requestOptions = this;
// View's scale type.
// 处理图片缩放,根据缩放类型来初始化对应的requestOptions对象
......
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor() // 运行在主线程的handler
);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 上面代码段首先处理图片缩放类型(裁剪、对齐方式等),并将生成的相关参数放入了requestOptions对象中,然后再将其作为参数传给了RequestBuilder.java类私有方法into。该方法定义的四个参数分别为:viewTarget、target回调监听器、请求参数、主线程的回调函数;
- 显然外部传入ImageView对象最终被转换成了ViewTarget对象,转换函数便是glideContext.buildImageViewTarget(view, transcodeClass);
// GlideContext.java
public <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
- 1.
- 2.
- 3.
- 4.
ViewTarget又是由ImageViewTargetFactory工厂方法生成,接着再看buildTarget方法是如何生成ViewTarget对象。
// imageViewTargetFactory.java
public class ImageViewTargetFactory {
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view, @NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as(Class).transcode(ResourceTranscoder)");
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
可以看到无论传入参数是何种类型,最终都会转换成两种类型的ViewTarget :BitmapImageViewTarget;DrawableImageViewTarget;这里如何选择取决于asBitmap()、asGif()和asDrawable()函数是否被调用,默认是Bitmap类型,所以这里默认返回的是BitmapImageViewTarget;
// BitmapImageViewTarget.java
public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
public BitmapImageViewTarget(ImageView view) {
super(view);
}
@Override
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource); // 显示图片
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
至此ViewTarget创建完毕,我们再回到RequestBuilder.java私有into方法
// RequestBuilder.java`
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
// 注释1:创建request
Request request = buildRequest(target, targetListener, options, callbackExecutor);
// 获取前一个reqeust请求对象
Request previous = target.getRequest();
// 与上一个请求相同 并且 上一个请求已完成
if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
// 上一个请求已完成,那么重新启动它
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
// 与上一个请求不同,则清除掉上一个,再将加入新请求
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
顺着代码次序,来看看这个方法每一步都干了什么:
- 首先执行buildRequest方法创建一个新的Request请求req1;
- 获取当前ViewTarget上正在进行中的Request请求req2;
- 判断新建的请求req1与已有的请求req2是否相同,如果相同则判断是否跳过req2请求的缓存,两个条件都满足则开始执行begin()方法开始请求资源并停止往下执行,条件都不满足则继续执行第四步;
- 给ViewTarget设置最新的请求req1,然后执行track方法追踪req1。
- 执行into(view)方法首先获取到了Request请求,然后开始执行Request。如果是复用的Request则直接执行begin(),否则执行track(target, request),但最终仍然会执行begin();
// ReqeustManager.java
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
// 与lifecycle绑定
targetTracker.track(target);
// 启动reqeust
requestTracker.runRequest(request);
}
// RequestTracker.java
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin(); // 立即开始加载
} else {
//防止从以前的请求中加载任何位图,释放该请求所拥有的任何资源,显示当前占位符(如果提供了该占位符),并将该请求标记为已取消。
// request.java( Interface )
request.clear();
pendingRequests.add(request); // 加入队列等待执行
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- track方法的源码,先是执行targetTracker.track(target)监听ViewTarget的请求,然后runRequest开始执行。由于最终都是通过begin()方法开始请求,所以我们先来看看begin()方法的具体实现;
- Request类是interface类型,begin()它的抽象方法,所以我们要想弄清楚begin()的具体实现,那就要先找到Request的实现类,从buildRequest(xx)方法入手,同样先贴出源码:
// RequestBuilder.java
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
return buildRequestRecursive(
/*requestLock=*/ new Object(),
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
private Request buildRequestRecursive(
Object requestLock,
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
ErrorRequestCoordinator errorRequestCoordinator = null;
// 请求出错了
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
// 无法确认完成请求和缩略图请求哪个先完成,所以当缩略图比完成请求后完成时就不再显示缩略图
Request mainRequest =
buildThumbnailRequestRecursive(
requestLock,
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
// 请求成功了,直接返回缩略图Request
if (errorRequestCoordinator == null) {
return mainRequest;
}
// ...
Request errorRequest =
errorBuilder.buildRequestRecursive(
requestLock,
target,
targetListener,
errorRequestCoordinator,
errorBuilder.transitionOptions,
errorBuilder.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder,
callbackExecutor);
// 同时返回缩略图请求和错误请求
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
- 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.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
显然代码里的mainRequest就是我们要找的Request了,它是由buildThumbnailRequestRecursive方法返回的,深入其内部我们发现Request最终其实是由SingleRequest.obtain方法产生,也就是说我们最终拿到的Request其实就是SingleReqeust类的一个实例。这里过程比较简单,代码就不贴出来了。我们直接去SingleReqeust类里面 看看begin方法如何实现的;
// SingleReqeust.java
public void begin() {
if (status == Status.COMPLETE) {
// 资源已下载,直接回调
// 执行动画
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// 计算尺寸
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
// 开始加载
// 设置占位度
target.onLoadStarted(getPlaceholderDrawable());
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
进入begin方法后首先判断如果资源已经过加载好了则直接回调onResourceReady显示图片并缓存,否则测量出图片尺寸后再开始加载图片(onSizeReady()中执行加载任务)并同时显示占位图:
①overrideWith、overrideHeight通过override(width, height)设置:
Glide.with(mContext).load(url).override(75, 75).into(imageView);
②占位图是用户调用placeholder(resId)设置:
Glide.with(mContext).load(url).placeholder(resId).into(imageView);
接着再看onSizeReady()测量完图片尺寸后如何加载图片的:
// SingleRequest.java
@Override
public void onSizeReady(int width, int height) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
// 获取图片尺寸
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
// 开始加载任务
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
}
- 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.
- 可以看到真正的下载任务是在Engine类的load方法中实现的,其中也涉及到了图片缓存逻辑;
- 最终通过Handler机制,Glide从工作线程切换到主线程,并最终将Drawable对象显示到ImageView上;
总结
Glide初始化、显示占位图、图片封面的整个业务流程都走完了;
可以从中学习glide中的设计模式:单例模式、工厂方法、策略模式等等,发现自己的不足之处;
Glide的图片缓存可以看上一篇文章