一直想写一个篇 Glide原理讲解,可一直没能动笔。
不是因为没时间,是 Glide 涉及太过广泛,内部逻辑太过犀利。一直没能找到一个合适的制高点来俯览全身。
之前对 Glide 一直停留在使用层面,只晓得使用方便,链式调用,节约内存,无所顾及内部原理,更没能学习其架构设计。
接下来我将通过以下几个问题来引伸分析Glide的内部逻辑,尽量做到 由使用到原理,从架构到逻辑,深入浅出。
- Glide是干啥的?
- 为什么用Glide?
- Glide怎么用?
- Glide包含哪些模块?都干什么用的?
- Glide调用某一个方法后具体干了什么事?
本文基于Glide的最新版本 4.10.0 进行分析,如有错误欢迎指出。
注:最新版本是4.11.0,发晚了,但是4.10.0 也算是非常非常新的了。
960页全网最全Android开发笔记:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题等等,帮助大家深刻理解Android相关知识点的原理以及面试相关知识。
1. 请用一脸懵逼的姿势了解,Glide到底是干啥的?
Glide is a fast and efficient open source media management and image loading framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.
翻译成能听懂的
Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。
2. 那你为什么要用Glide?
有个不知道哪的人,不知道叫什么名的人曾经说过,当你开始问 为什么 的时候,那么你可能要变身了。
这个问题非常简洁,但分析透彻实属不易,甚至该问题是伴随使用的需求,场景变化而变化的。既然不好直观的分析说明,我们就罗列对应优势,方便需要的时候对号入座
- 优雅的调用姿 Glide.with(fragment).load(url).into(imageView);
- 多维度的图片格式(png,jpg,gif,webp,video等...)
- 健壮的身体素质 性能极好,能防止频繁主线程I/O,垃圾回收而导致的页面闪烁卡顿等。
- 灵活的加载请求 可根据页面的生命周期动态管理图片的加载请求。
- 风骚的缓存策略 支持按控件大小缓存对应大小的图片,且默认的图片格式为RGB_565,相对占用空间更小。速度也因此更快。
嗯~~~ 就看这几个,你说你要不要选Glide.....
3. Glide怎么用?
可算是到我闭着眼都能写的环节了,来来来,咱们撸代码。
首先增加如下引用。
代码语言:javascript复制implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
使用方式,我们罗列5种常用的使用方式:
代码语言:javascript复制//********************************** 直接使用 **********************************
Glide.with(Context).load("").into(mImageView)
//使用Generated API, 作用范围Application 模块内使用
//创建MyAppGlideModule类加上@GlideModule注解,buid后既能使用 GlideApp
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}
//Generated API加载图片
GlideApp.with(Context).load("").into(mImageView);
//********************************** 添加占位图 **********************************
RequestOptions requestOptions = new RequestOptions()
.placeholder(R.drawable.a)
.error(R.drawable.e)
.diskCacheStrategy(DiskCacheStrategy.NONE);//设置缓存了参数
Glide.with(Context).load("").apply(requestOptions).into(mImageView);
//Generated API 方式(和Glide3 一样)
GlideApp.with(Context).load("")
.placeholder(R.drawable.a)
.error(R.drawable.e)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(mImageView);
// 后备回调符(Fallback) Generated API 方式才有
//在设置图片的场景中,如果用户不设置,也就是为null的情况,可使用后备回调符显示默认图
private static final String NULL_URL=null;
GlideApp.with(Context).load(NULL_URL)
.fallback(R.drawable.a)
.into(mImageView);
//********************************** 指定加载图片的大小(override) **********************************
RequestOptions requestOptions = new RequestOptions().override(200,100);
Glide.with(Context).load("").apply(requestOptions).into(mImageView);
//Generated API 方式
GlideApp.with(Context).load("")
.override(200,100)
.into(mImageView);
//********************************** 通过thumbnail加载缩略图 **********************************
//与placeholder类似,但是thumbnail可加载网络图,placeholder只能加载本地图。
RequestOptions requestOptions = new RequestOptions()
.override(200,100)
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(MainActivity.this)
.load(url)
.thumbnail( Glide.with(this)
.load(url)
.apply(requestOptions))
.into(iv);
//Generated API 方式
GlideApp.with(Context)
.load(url)
.thumbnail( GlideApp.with(this)
.load(IMAGE_URL).override(200,100)
.diskCacheStrategy(DiskCacheStrategy.NONE))
.into(mImageView);
//********************************** 设置图片的变化操作 **********************************
//可通过
//CenterCrop(图片原图的中心区域进行裁剪显示)
//FitCenter(图片原始长宽铺满)
//CircleCrop(圆形裁剪)
//设置图片的变化操作
Glide.with(Context)
.load(IMAGE_URL)
.apply(RequestOptions.circleCropTransform())
.into(mImageView);
//Generated API 方式
GlideApp.with(Context).load(IMAGE_URL)
.circleCrop()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(mImageView);
4. Glide包含哪些模块?都干什么用的?
接下来的环节将是本篇文章的高地。
我们可以先简单思考下,如果你是Glide的作者,你要怎么设计一个优秀的图片加载框架,需要兼顾哪些环节,需要包含哪些模块?
简单来说,图片加载需要以下几种模块。
- 参数封装
- 加载请求
- 执行引擎
- 数据加载器
- 解码器
- 编码器
- 缓存
那实际Glide是这样设计的么?
- 参数封装(获得请求的参数,配置加载图的属性)
- 网络请求(下载图片)
- 缓存逻辑(资源重用)
- 加解码(处理图片)
- 数据加载(判断加载方式/请求网络/IO读取/内存读取)
- 线程池/线程队列(或者叫任务队列,用于处理每次的加载任务)
- 防止OOM的处理机制(软引用,缓存,压缩,存储等..)
- 生命周期管理(防止内存泄露)
- .......
说了这么多,那加载一张图片,到底是怎样一个流程呢?换句话说,这些模块具体是怎样协同工作的,调用顺序又是怎样的呢?
给大家画了一个简单的图,兄弟们,这个图来之不易啊,你不从头捋一遍你真是不知道咋画啊...
好多小伙伴可能看到这个图,又懵逼了, 你这都啥跟啥啊,一个Glide你画这么几个块就over了?
那网络咋加载的?那缓存怎么处理的,那线程咋切换的?那生命周期咋控制的。啥也没说啊..
各位,各位,先别着急。咱们力争的是先 整体再局部,先总览再细分。接下来咱们针对代码进行跟进。
兄台准备好面对疾风了么?
5. Glide调用某一个方法后具体干了什么事?
首先我们拿一个简单的使用进行跟进
代码语言:javascript复制Glide.with(context).load("").into(mImageView)
链式调用,我们分解为以下三个方法进行分析。
Glide.with(context) Glide.with(context).load("") Glide.with(context).load("").into(mImageView)
5.1. Glide.with(context)具体干了啥?
死亡如风,常伴吾身。我会给你个痛快的!
代码语言:javascript复制/**
* 简单来说该方法进行了以下几步操作
* 1. Glide 初始化
* 2. 各种线程池初始化,各种缓存对象初始化
* 3. Engine初始化,至于Engine是啥,后续我们慢慢介绍
* 4. RequestManager 初始化
* 5. 创建透明的fragment,动态监听生命周期
*/
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// 检测传入的context是否合规
Preconditions.checkNotNull(context, "You cannot start .....");
return Glide.get(context).getRequestManagerRetriever();
}
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
//如果glide为null则启动检测初始化逻辑
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
private static void checkAndInitializeGlide(@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
// 如果已经在初始化中了,则抛异常。
if (isInitializing) {
throw new IllegalStateException( "You cannot call Glide.get() in registerComponents(), use the provided Glide instance instead");
}
//设置初始化状态
isInitializing = true;
initializeGlide(context, generatedAppGlideModule);
isInitializing = false;
}
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
...
//调用GlideBuilder.build()方法进行Glide初始化
Glide glide = builder.build(applicationContext);
...
//注册回调
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
//GlideBuilder.build()方法,用于初始化各种线程池,以及缓存对象,初始化Glide
Glide build(@NonNull Context context) {
//创建资源池
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
//硬盘缓存池
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
//初始化Engine
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);
//初始化Glide
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled,
isImageDecoderEnabledForBitmaps,
hardwareBitmapFdLimit,
minHardwareDimension);
}
//RequestManagerRetriever.get()通过Glide.with(Context)入参获取FragmentManager
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
//通过Android版本>17,并且页面已经isDestroyed则抛出异常
assertNotDestroyed(activity);
//通过入参获取FragmentManager
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
//RequestManagerRetriever.supportFragmentGet()
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
//获取请求fragment
SupportRequestManagerFragment current =getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
//获取fragment中RequestManager 对象,如果fragment是刚刚创建的requestManager 为null
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
//创建requestManager 并设置给fragment
requestManager =
factory.build( glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
//先查找这个tag的fragment有没有被创建
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//从pendingSupportRequestManagerFragments中在查找一次
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
//如果都为null则new创建
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
//存储
pendingSupportRequestManagerFragments.put(fm, current);
//添加到FragmentManager 中
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//发送通知
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
兄弟们,咱回回神,经过这一通暴讲,我们终于把 with() 这个方法分析完成了。
是不是贼拉牛逼。现在对with()方法的流程以及生命周期的控制管理是不是稍稍有那么一些印象了。无法复述没有关系。先能顺下来,当你第二遍看到时候就会事半功倍了。
写了这么一堆,我们回顾下这个with()干了啥?
1. 调用getRetriever(activity).get(activity)
2. 调用checkAndInitializeGlide(context, annotationGeneratedModule)检测并启动初始化
3. 调用initializeGlide(context, generatedAppGlideModule);连带调用GlideBuilder.build(applicationContext)进行Glide初始化
4. Glide初始化过程中会创建各种线程池,各种缓存对象,并初始化Engine对象。
5. 调用RequestManagerRetriever.get(activity)返回RequestManager对象,同时通过入参的activity.getSupportFragmentManager()获取FragmentManager对象。
6.调用supportFragmentGet(activity, fm, null, isActivityVisible(activity))创建RequestManager和SupportRequestManagerFragment并做关联。
这个地方需要注意下,可能调用的是fragmentGet()或getApplicationManager(),最终目的都是 创建RequestManager。
主线程下调用各个对象之间的对应关系为
1个Activity<-->1个FragmentManager<-->1个RequestManagerFragment<--> 1个RequestManager。
子线程调用会传入ApplicationContext,此时使用的是全局的单例RequestManager,生命周期和App一致。
扩展个知识点,具体Glide是怎么依赖生命周期动态控制加载图片的呢?
SupportRequestManagerFragment对象中有个属性为ActivityFragmentLifecycle对象,并且在SupportRequestManagerFragment的生命周期方法中均调用了ActivityFragmentLifecycle的对应方法。
代码语言:javascript复制 @Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
unregisterFragmentWithRoot();
}
然后通过LifecycleListener调用对应的方法
代码语言:javascript复制void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}
void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
我们再看都哪些类实现了LifecycleListener接口呢?其中有RequestManager,TargetTracker和Target(继承)实现了该接口,我们抽出RequestManager单独看下。
RequestManager
代码语言:javascript复制 @Override
public synchronized void onStart() {
resumeRequests();
targetTracker.onStart();
}
/**
* Lifecycle callback that unregisters for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
*/
@Override
public synchronized void onStop() {
pauseRequests();
targetTracker.onStop();
}
/**
* Lifecycle callback that cancels all in progress requests and clears and recycles resources for
* all completed requests.
*/
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
public synchronized void resumeRequests() {
requestTracker.resumeRequests();
}
public void pauseRequests() {
isPaused = true;
for (Request request : Util.getSnapshot(requests)) {
//如果正在执行过程中
if (request.isRunning()) {
// Avoid clearing parts of requests that may have completed (thumbnails) to avoid blinking
// in the UI, while still making sure that any in progress parts of requests are immediately
// stopped.
//调用暂停
request.pause();
//添加到挂起的请求中。
pendingRequests.add(request);
}
}
}
public void clear(@Nullable final Target<?> target) {
if (target == null) {
return;
}
untrackOrDelegate(target);
}
private void untrackOrDelegate(@NonNull Target<?> target) {
boolean isOwnedByUs = untrack(target);
// We'll end up here if the Target was cleared after the RequestManager that started the request
// is destroyed. That can happen for at least two reasons:
// 1. We call clear() on a background thread using something other than Application Context
// RequestManager.
// 2. The caller retains a reference to the RequestManager after the corresponding Activity or
// Fragment is destroyed, starts a load with it, and then clears that load with a different
// RequestManager. Callers seem especially likely to do this in retained Fragments (#2262).
//
// #1 is always an error. At best the caller is leaking memory briefly in something like an
// AsyncTask. At worst the caller is leaking an Activity or Fragment for a sustained period of
// time if they do something like reference the Activity RequestManager in a long lived
// background thread or task.
//
// #2 is always an error. Callers shouldn't be starting new loads using RequestManagers after
// the corresponding Activity or Fragment is destroyed because retaining any reference to the
// RequestManager leaks memory. It's possible that there's some brief period of time during or
// immediately after onDestroy where this is reasonable, but I can't think of why.
Request request = target.getRequest();
if (!isOwnedByUs && !glide.removeFromManagers(target) && request != null) {
//移除请求调用清理。
target.setRequest(null);
request.clear();
}
}
看到以上及其简短的注释,估计你是明白咋回事了。
本质原理就是 SupportRequestManagerFragment是通过传入的Context创建的。可以感知onStart(), onStop(), onDestroy() 等方法。
在这些方法中调用 ActivityFragmentLifecycle 中对应的方法,然后 循环 找到 ActivityFragmentLifecycle中与之对应的RequestManager对象,再调用 对应生命周期 的方法。
最后通过requestTracker循环找到对应的Request对象,然后调用对应的处理方法从而达到了根据生命周期动态控制图片加载的目的。
好了,我就不多赘述了。我还得抓紧赶路,争取一气呵成。
5.2. Glide.with(context).load("")具体干了啥?
长路漫漫 惟剑做伴 吾之荣耀 离别已久 宁日安在 无人能云 且随疾风前行
Glide.with(context)已经返回了RequestManager,因此Glide.with(context).load("")相当于RequestManager.load("");
代码语言:javascript复制public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
//注意返回值为RequestBuilder
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
//初始化RequestBuilder对象
return new RequestBuilder<>(glide, this, resourceClass, context);
}
//RequestBuilder的构造方法
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class<TranscodeType> transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
//初始化RequestListeners
initRequestListeners(requestManager.getDefaultRequestListeners());
apply(requestManager.getDefaultRequestOptions());
}
private void initRequestListeners(List<RequestListener<Object>> requestListeners) {
for (RequestListener<Object> listener : requestListeners) {
addListener((RequestListener<TranscodeType>) listener);
}
}
public RequestBuilder<TranscodeType> apply(@NonNull BaseRequestOptions<?> requestOptions) {
Preconditions.checkNotNull(requestOptions);
return super.apply(requestOptions);
}
上文中的源码我们只是分析load()方法的第一步,首先分析了load()中asDrawable()方法以及一系列的连带方法。
逻辑非常简单,相对比较有代表性的是初始化了RequestBuilder对象。
我们继续分析RequestBuilder.load()
代码语言:javascript复制public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
诶?这就完了?咋稍稍有点不适应呢?就只设置了下model和isModelSet属性就完事了? 看来是的,load()就是这么简单,需要注意的一点是model通过Object接收的。
5.3. Glide.with(context).load("").into(mImageView)具体干了啥?
回首往昔 更进一步 吾虽浪迹天涯 却从未迷失本心。最后一个方法了,黎明就在眼前 Glide.with(context).load("")的返回值为RequestBuilder,因此无亚于RequestBuilder.into(mImageView);
代码语言:javascript复制// 注意返回值为ViewTarget
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
//判断是否为主线程,不是主线程则抛异常
Util.assertMainThread();
//判空逻辑
Preconditions.checkNotNull(view);
//这个requestOptions 是啥现在还看不出来,好像是存放图片展示的相关属性的。
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// 在此方法中克隆,以便如果使用此RequestBuilder加载到视图中,然后加载到其他目标中,
//则不会保留基于上一个视图的缩放类型应用的转换。
//该switch 主要用于控制调用Glide时设置的各种样式,没设置则为默认的
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
//注意这个transcodeClass在上述4.2中得知transcodeClass为Drawable.class
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
//
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
//由于为Drawable.class因此本方法将返回DrawableImageViewTarget对象。
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)");
}
}
简单来说上述的的一串逻辑可以理解为
a. 调用into()后首先判断是否为主线程 b. 通过view.getScaleType()判断使用哪个requestOptions对象,如果没设置则使用默认的。 c. 调用glideContext.buildImageViewTarget(view, transcodeClass)返回DrawableImageViewTarget对象。
ok,我们继续看into方法,现在的into可以理解为
代码语言:javascript复制into(DrawableImageViewTarget,null,requestOptions,Executors.mainThreadExecutor());
代码语言:javascript复制 private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
//还是检查逻辑
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//创建Request对象,看面相 这个应该还是用于请求的对象,我们得看看咋创建的
Request request = buildRequest(target, targetListener, options, callbackExecutor);
//此处的target为DrawableImageViewTarget
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;
}
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);
}
//此时的target为DrawableImageViewTarget,targetListener为null
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) {
// 如果需要,请先构建ErrorRequestCoordinator,以便我们可以更新parentCoordinator。
//但是因为我们没有设置error,因此errorBuilder 为null
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
//我们后续详细看下这个mainRequest创建逻辑
Request mainRequest = buildThumbnailRequestRecursive(
requestLock,
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
//因为errorBuilder 为null因此上述的errorRequestCoordinator也为null,此处直接返回mainRequest了
if (errorRequestCoordinator == null) {
return mainRequest;
}
int errorOverrideWidth = errorBuilder.getOverrideWidth();
int errorOverrideHeight = errorBuilder.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight) && !errorBuilder.isValidOverride()) {
errorOverrideWidth = requestOptions.getOverrideWidth();
errorOverrideHeight = requestOptions.getOverrideHeight();
}
Request errorRequest = errorBuilder.buildRequestRecursive(
requestLock,
target,
targetListener,
errorRequestCoordinator,
errorBuilder.transitionOptions,
errorBuilder.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder,
callbackExecutor);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
buildThumbnailRequestRecursive()方法的逻辑比较长,我们单独拿出来去分析,但是其实我们并没有设置thumbnail(),所以thumbnailBuilder和thumbSizeMultiplier 是都null,只会走最后一个else。
我贴下整体的逻辑代码,不要惊慌,这一大片代码,只为最后创建个SingleRequest。
代码语言:javascript复制private Request buildThumbnailRequestRecursive(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
if (thumbnailBuilder != null) {
....
return coordinator;
} else if (thumbSizeMultiplier != null) {
....
return coordinator;
} else {
// 没有设置thumbnail走
return obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
}
}
private Request obtainRequest(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
return SingleRequest.obtain(
context,
glideContext,
requestLock,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
public static <R> SingleRequest<R> obtain(
Context context,
GlideContext glideContext,
Object requestLock,
Object model,
Class<R> transcodeClass,
BaseRequestOptions<?> requestOptions,
int overrideWidth,
int overrideHeight,
Priority priority,
Target<R> target,
RequestListener<R> targetListener,
@Nullable List<RequestListener<R>> requestListeners,
RequestCoordinator requestCoordinator,
Engine engine,
TransitionFactory<? super R> animationFactory,
Executor callbackExecutor) {
return new SingleRequest<>(
context,
glideContext,
requestLock,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
engine,
animationFactory,
callbackExecutor);
}
private SingleRequest(
Context context,
GlideContext glideContext,
@NonNull Object requestLock,
@Nullable Object model,
Class<R> transcodeClass,
BaseRequestOptions<?> requestOptions,
int overrideWidth,
int overrideHeight,
Priority priority,
Target<R> target,
@Nullable RequestListener<R> targetListener,
@Nullable List<RequestListener<R>> requestListeners,
RequestCoordinator requestCoordinator,
Engine engine,
TransitionFactory<? super R> animationFactory,
Executor callbackExecutor) {
this.requestLock = requestLock;
this.context = context;
this.glideContext = glideContext;
this.model = model;
this.transcodeClass = transcodeClass;
this.requestOptions = requestOptions;
this.overrideWidth = overrideWidth;
this.overrideHeight = overrideHeight;
this.priority = priority;
this.target = target;
this.targetListener = targetListener;
this.requestListeners = requestListeners;
this.requestCoordinator = requestCoordinator;
this.engine = engine;
this.animationFactory = animationFactory;
this.callbackExecutor = callbackExecutor;
status = Status.PENDING;
if (requestOrigin == null && glideContext.isLoggingRequestOriginsEnabled()) {
requestOrigin = new RuntimeException("Glide request origin trace");
}
}
生成完SingleRequest了,看到头了,是不是突然不知道从哪再次开始了?
还记得RequestBuilder.into()方法的最后那三个方法么?这就是重新开始的地方....
代码语言:javascript复制 private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
//还是检查逻辑
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//创建Request对象,看面相 这个应该还是用于请求的对象,我们得看看咋创建的
Request request = buildRequest(target, targetListener, options, callbackExecutor);
//此处的target为DrawableImageViewTarget
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;
}
我们一个一个跟进,先看下clear()
代码语言:javascript复制 public void clear(@Nullable final Target<?> target) {
if (target == null) {
return;
}
untrackOrDelegate(target);
}
private void untrackOrDelegate(@NonNull Target<?> target) {
boolean isOwnedByUs = untrack(target);
// 如果在启动请求的请求管理器之后清除了目标,我们将在这里结束
// 被摧毁了。至少有两个原因:
// 1. 我们在后台线程上调用clear(),使用的不是应用程序上下文请求管理器。
// 2. 调用方在相应的活动或片段被破坏,用它开始加载,然后用另一个请求管理器。调用者似乎特别有可能在保留的片段中这样做(#2262)。
//
// #1 总是一个错误。充其量,调用者只是在像异步任务。最坏情况下,调用者在持续的
// 如果他们做了类似引用活动请求管理器的长时间操作后台线程或任务。
//
// #2 总是一个错误。调用方不应该在由于保留对请求管理器泄漏内存。可能有一段很短的时间
// 紧接着onDestroy这是合理的。
Request request = target.getRequest();
if (!isOwnedByUs && !glide.removeFromManagers(target) && request != null) {
target.setRequest(null);
request.clear();
}
}
clear() 相对比较简单,判断当前target上面是否有请求,进行一些逻辑判断是否需要取消。 然后DrawableImageViewTarget的Request对象置空。
target.setRequest(request);没啥好说的只是将Request设置到DrawableImageViewTarget对象中。
我们看下最后的 requestManager.track(target, request);
代码语言:javascript复制//track()内部又调用两个方法。
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
//TargetTracker.track()比较简单,将DrawableImageViewTarget添加到targets 的Set<Target<?>>对象中。
private final Set<Target<?>> targets = Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());
public void track(@NonNull Target<?> target) {
targets.add(target);
}
private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
//RequestTracker.runRequest()
public void runRequest(@NonNull Request request) {
//首先将request对象添加到Set<Request>对象中统一管理
requests.add(request);
if (!isPaused) {
//如果界面未处于onStop状态,代表页面可见
request.begin();
} else {
//如果界面处于onStop状态,页面不可见
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
//添加到pendingRequests对象中
//pendingRequests的注释有写到,pendingRequests用于存未完成的请求,方便下次请求,该列表用于维护强引用,确保再次运行前不被GC,
pendingRequests.add(request);
}
}
我们继续看下以上代码中的 request.begin() 方法,此处的requests为SingleRequest对象,因此只需看SingleReques.begin();
代码语言:javascript复制public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
//首先对model 判断,model 为load传入的那个url。
if (model == null) {
//如果为null则直接失败。
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
//当用户设置了fallback drawable,输入详细日志,因为fallback drawable偶尔需要空models
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
//如果当前状态已经为运行状态了,则抛异常
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
//如果完成后重新再加载,(一般通过notifyDataSetChanged这样的方法,在同一个Target或View中启动 相同的请求)
//我们可以简单地使用上次检索到的资源和大小,跳过获取新大小,启动新加载等环节。
//由于视图大小可能已更改,此时希望重新启动加载的用户需要在启动加载之前手动清除View或Target。
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
//对于既没完成也不正在运行的请求,可以将其重新启动视为新请求。
status = Status.WAITING_FOR_SIZE;
//判断宽高是狗有效。
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());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " LogTime.getElapsedMillis(startTime));
}
}
}
2个方法了
onSizeReady(overrideWidth, overrideHeight); target.onLoadStarted(getPlaceholderDrawable());
兄弟们,快了,这次真快了...再坚持一下子。
诶呀妈呀,可算是看到胜利的曙光了,我太难了。。。。
代码语言:javascript复制 public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " LogTime.getElapsedMillis(startTime));
}
//检测下当前是否为等待状态。
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " LogTime.getElapsedMillis(startTime));
}
//说的天花乱坠的,这里才是加载的核心,缓存请求,都在这里,我单独开一篇去讲解,要不然我怕你们坚持不住了。
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);
// This is a hack that's only useful for testing right now where loads complete synchronously
// even though under any executor running on any thread but the main thread, the load would
// have completed asynchronously.
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " LogTime.getElapsedMillis(startTime));
}
}
//ImageViewTarget.onLoadStarted()加载完成
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
setResourceInternal(null);
setDrawable(placeholder);
}
public void setDrawable(Drawable drawable) {
view.setImageDrawable(drawable);
}
这头皮发麻的逻辑总算是喷....嗯..不对,总算是讲完了。
你能看到这我敬你是条汉子,能有幸学习这么优秀的框架真的是荣幸至极。一次不懂没有关系。重要的是那颗学习的心。
多了也不说了,最后一句。
纵然前路迷茫,纵然天隔一方,纵然我手中的刀刃已然破碎,也无法阻挡我寻找你的道路。吾之初心,永世不忘,这个故事还没有完结。
最后
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2019-2020BAT 面试真题解析,我把大厂面试中常被问到的技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 诸多细节。
还有 高级架构技术进阶脑图 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
我是之后按着这份资料复习面试题,后来再去面试就过了,帮助还是很大的~
以上内容均放在了开源项目:【github】 中已收录,里面包含不同方向的自学Android路线、面试题集合/面经、及系列技术文章等,资源持续更新中...
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。