写点什么

Android | 《看完不忘系列》之 Glide

用户头像
哈利迪
关注
发布于: 2020 年 07 月 15 日
Android | 《看完不忘系列》之Glide

《看完不忘系列》将以从树干到细枝的思路来分析一些技术框架,本文是开篇文章,将对开源项目Glide图片加载库进行介绍。如果老铁们看完还是忘了,就 ~~回来揍我一顿~~ 点赞收藏加关注,多看两遍~





概览



基于Glide最新版本4.11.0,未迁AndroidX的项目只能使用4.9.0,简单使用:



引入依赖,app/build.gradle:



implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'



一句代码,完成图片加载:



Glide.with(this) //指定上下文,可以是app、activity、fragment
.load(url) //网络图片地址
.into(img); //用于展示的imageView



用起来简洁优雅,然后我们先大致预览下Glide的一些职能,





树干:核心流程



Glide.with(this).load(url).into(img)为起点,拆成with购车load上牌into发车三个环节来分析。



with:购车



简单来说,with的功能就是根据传入的上下文context来获取图片请求管理器RequestManager,他用来管理和启动图片请求,





context可以传入app、activity、fragment,这决定了图片请求的生命周期。通常是使用粒度较细的context,即使用当前页面的context而不是全局的app。这样做的好处是,打开一个页面开启图片加载,然后退出页面,图片请求就会跟随页面销毁而被取消,而不是继续加载而浪费资源。



当context是app时,通过RequestManagerRetriever获得的RequestManager是一个全局单例,这类图片请求的生命周期将会跟随整个app,



class RequestManagerRetriever implements Handler.Callback {
volatile RequestManager applicationManager;
RequestManager getApplicationManager(Context context) {
//双重检查锁,获取单例的RequestManager
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
Glide glide = Glide.get(context.getApplicationContext());
applicationManager = factory.build(glide,new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),context.getApplicationContext());
}
}
}
//返回应用级别的RequestManager单例
return applicationManager;
}
}



当context是Activity时,创建一个SupportRequestManagerFragment(无界面的空fragment)添加到Activity,从而感知Activity的生命周期,同时创建RequestManager给空fragment持有,



class RequestManagerRetriever implements Handler.Callback {
RequestManager supportFragmentGet(Context context,FragmentManager fm,
Fragment parentHint,boolean isParentVisible) {
//获取空fragment,无则创建
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
//如果空fragment没有RequestManager,就创建一个
requestManager = factory.build(glide, current.getGlideLifecycle(),
current.getRequestManagerTreeNode(), context);
//让空fragment持有RequestManager
current.setRequestManager(requestManager);
}
//返回页面级别的RequestManager
return requestManager;
}
}



然后看到getSupportRequestManagerFragment方法,



class RequestManagerRetriever implements Handler.Callback {
Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments =
new HashMap<>();
SupportRequestManagerFragment getSupportRequestManagerFragment(
final FragmentManager fm, Fragment parentHint, boolean isParentVisible) {
//通过tag找到Activity中的空fragment
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//findFragmentByTag没找到空fragment,有可能是延迟问题?再从Map中找一下
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
//确实没有空fragment,就创建一个
current = new SupportRequestManagerFragment();
//...
//缓存进Map
pendingSupportRequestManagerFragments.put(fm, current);
//空fragment添加到Activity,使其能感知Activity的生命周期
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//...
}
}
return current;
}
}



综上,通过with操作,



当context是app时,得到应用级别RequestManager全局单例;



当context是Activity时,每个页面都会被添加一个空fragment,由空fragment持有页面级别RequestManager



注意:如果with发生在子线程,不管context是谁,都返回应用级别RequestManager单例。

>

发散:添加空fragment来感知页面生命周期的思想,在Lifecycle的实现中也可以看到,见ReportFragmentinjectIfNeededIn方法。(不过这个方法在Lifecycle的2.2.0版本中有所改动,Android 10开始的设备改成了使用Application.ActivityLifecycleCallbacks来感知,感兴趣可以康康)



至此,我们根据存款context买到了心仪的车RequestManager,下面开始上牌~





load:上牌



load方法得到了一个RequestBuilder图片请求构建器,见名知意猜一下,是用来创建图片请求的,





class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>> {
RequestBuilder<Drawable> load(String string) {
return asDrawable().load(string);
}
RequestBuilder<Drawable> asDrawable() {
//需要加载的类型为Drawable
return as(Drawable.class);
}
<ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
//创建一个请求构建器
return new RequestBuilder<>(glide, this, resourceClass, context);
}
}



然后跟进asDrawable().load(string)



class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
RequestBuilder<TranscodeType> load(String string) {
return loadGeneric(string);
}
RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
//只是简单地赋值
this.model = model;
isModelSet = true;
return this;
}
}



到这里,我们就完成了上牌,得到了一个RequestBuilder图片请求构建器。没错,上牌就这么简单,毕竟摇号已经够艰难了对吧?





into:发车



阶段一



来到into了,



class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
ViewTarget<ImageView, TranscodeType> into(ImageView view) {
//...
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
//根据ImageView的ScaleType,来配置参数
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
//...
}
}
return into(
//前面提到,Target是展示图片的载体,这里他封装了ImageView
glideContext.buildImageViewTarget(view, transcodeClass),null,
requestOptions,Executors.mainThreadExecutor());
}
}



继续跟进into重载方法,



class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
<Y extends Target<TranscodeType>> Y into(Y target,RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,Executor callbackExecutor) {
//...
//创建图片请求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
//获取Target载体已有的请求
Request previous = target.getRequest();
//如果两个请求等效,并且xxx(先忽略)
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
//启动异步请求
previous.begin();
}
return target;
}
requestManager.clear(target);
//图片载体绑定图片请求,即imageView setTag为request
target.setRequest(request);
//启动异步请求
requestManager.track(target, request);
return target;
}
}



跟进requestManager.track(target, request)



//RequestManager.java
void track(Target<?> target,Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
//RequestTracker.java
void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
//开启图片请求
request.begin();
} else {
request.clear();
//如果处于暂停状态,就把请求存起来,晚些处理
pendingRequests.add(request);
}
}



到这里,就启动了图片请求,





阶段二



那么,接下来重点关注的就是request.begin()了,



class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {
void begin() {
synchronized (requestLock) {
//...
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
//如果已经有了明确的尺寸,开始加载
onSizeReady(overrideWidth, overrideHeight);
} else {
//没有的话先去获取尺寸,最终还是走onSizeReady
target.getSize(this);
}
//...
}
}
void onSizeReady(int width, int height) {
synchronized (requestLock) {
//...
//engine.load,传了很多参数
loadStatus =
engine.load(glideContext,model,
requestOptions.getSignature(),
this.width,this.height,
requestOptions.getResourceClass(),
transcodeClass,priority,
requestOptions.getDiskCacheStrategy(),
//...
this,callbackExecutor);
//...
}
}
}



跟进engine.load



class Engine implements EngineJobListener,MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
<R> LoadStatus load(
GlideContext glideContext,
//...
Executor callbackExecutor) {
//...
EngineResource<?> memoryResource;
synchronized (this) {
//从内存加载
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
//如果内存里没有缓存,则加载
return waitForExistingOrStartNewJob(
glideContext,
model,
//...
key,
startTime);
}
}
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
<R> LoadStatus waitForExistingOrStartNewJob(...) {
//...
EngineJob<R> engineJob =engineJobFactory.build(...);
DecodeJob<R> decodeJob =decodeJobFactory.build(...);
jobs.put(key, engineJob);
//添加回调,这个cb就是SingleRequest自己,todo1
engineJob.addCallback(cb, callbackExecutor);
//engineJob开启decodeJob
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
}



DecodeJob是一个Runable,看看他的run方法,调用链如下:



DecodeJob.run -> DecodeJob.runWrapped -> DecodeJob.runGenerators ->

SourceGenerator.startNext -> SourceGenerator.startNextLoad ->

MultiModelLoader#MultiFetcher.loadData ->

HttpUrlFetcher.loadData



来到HttpUrlFetcher



class HttpUrlFetcher implements DataFetcher<InputStream> {
void loadData(Priority priority, DataCallback<? super InputStream> callback) {
try {
//获取输入流,没有引入okhttp,则使用HttpURLConnection
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
//回调出去
callback.onDataReady(result);
} catch (IOException e) {
callback.onLoadFailed(e);
} finally {
}
}
}



到这里,网络请求就完成了,下面看看图片是怎么设置上去的,前边留了个todo1,



//添加回调,这个cb就是SingleRequest自己,todo1



callback就是SingleRequest,被回调前有一些对象包装、解码操作,暂不深究,来到SingleRequest



class SingleRequest<R> implements Request, SizeReadyCallback, ResourceCallback {
void onResourceReady(Resource<?> resource, DataSource dataSource) {
Resource<?> toRelease = null;
try {
synchronized (requestLock) {
//...
Object received = resource.get();
//...
//这里
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
} finally {
if (toRelease != null) {
engine.release(toRelease);
}
}
}
void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
//...
try {
//...
//这里,回调给Target载体
target.onResourceReady(result, animation);
} finally {
}
notifyLoadSuccess();
}
}



跟进 target.onResourceReady,最终来到DrawableImageViewTarget



class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
void setResource(Drawable resource) {
//ImageView设置图片
view.setImageDrawable(resource);
}
}



结合阶段一和二,





终于,汽车发动起来了~





with购车load上牌into发车三个环节汇总起来,





可以看到,整个图片加载过程,就是with得到RequestManager,load得到RequestBuilder,然后into开启加载:



创建Request、开启Engine、运行DecodeJob线程、HttpUrlFetcher加载网络数据、回调给载体Target、载体为ImageView设置展示图片。



细枝:补充



线程切换



在子线程下载完图片后,如何回调主线程设置图片?在Executors类里,



private static final Executor MAIN_THREAD_EXECUTOR = new Executor() {
//主线程Handler
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable command) {
//切回主线程
handler.post(command);
}
};



调用栈如下:





线程池



GlideBuilder类里会初始化一些线程池:



Glide build(Context context) {
private GlideExecutor sourceExecutor; //加载图片
private GlideExecutor diskCacheExecutor; //管理磁盘缓存
private GlideExecutor animationExecutor; //管理动画
}



缓存



有内存缓存和磁盘缓存,在Engine.load时会去取,篇幅原因后面单独开篇来写。



webp动图



Fresco支持解析webp动图,Glide不支持,不过已经有了开源的方案,见GitHub - GlideWebpDecoder



选型



FrescoGlide怎么选?



Fresco具有一定侵入性,需要继承SimpleDraweeView



Fresco调用繁琐,没有Glide的链式调用优雅,当然这个可以包一层来解决;



Fresco在5.0以下的系统进行了内存优化(Ashmem区),这个优势在当下的环境已经不值一提,因为这些系统占比已经非常低了,一些App的minSDK都已经设置成21了。



所以,更推荐使用Glide(个人拙见,仅供参考)



尾声



作为《看完不忘系列》的文章,本文删减了很多源码,重点在于理清Glide图片加载流程,大家看的时候最好能跟着思路去阅读源码~然后,Glide还有解码、缓存的流程没有分析,后面会单独开篇来写。



参考资料










发布于: 2020 年 07 月 15 日阅读数: 152
用户头像

哈利迪

关注

. 2019.02.13 加入

公众号:哈利迪ei

评论

发布
暂无评论
Android | 《看完不忘系列》之Glide