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

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

概览

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

引入依赖,app/build.gradle:git

implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' 复制代码

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

Glide.with(this) //指定上下文,能够是app、activity、fragment
 .load(url) //网络图片地址  .into(img); //用于展现的imageView 复制代码

用起来简洁优雅,而后咱们先大体预览下Glide的一些职能,web

树干:核心流程

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,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还有解码、缓存的流程没有分析,后面会单独开篇来写。

参考资料


本文使用 mdnice 排版

相关文章
相关标签/搜索