Glide源码解析


序言

在Android开发过程中,图片处理是必不可少的,图片处理可繁可简。目前有也出现了很多优秀的图片加载库,如Glide、Picasso、Fresco等。
今天我们主要学习Glide的源码,Glide功能极为强大,支持图片的缓存策略,图片变换...
为了写这篇文章,除了自己阅读源码之外,也看了很多相关文章,其中极为推荐郭霖大神的文章

虽然已经有这么好的文章,但我觉得自己要研究这一块,也应该记录一下自己学习和理解的过程。

文章从以下几个主题展开介绍:

  • Glide的用法
  • Glide源码分析(主要流程)
  • Glide缓存机制
  • Glide图片变换
  • 对比Glide、Picasso、Fresco
  • 总结

Glide用法

本文基于Glide-4.7.1版本介绍。
Glide的用法很简单,按照下面几个步骤即可:

  1. 添加Gradle依赖
api 'com.github.bumptech.glide:glide:4.7.1'
api 'com.github.bumptech.glide:compiler:4.7.1'
api 'com.github.bumptech.glide:okhttp3-integration:4.7.1'
  1. 使用Glide
override fun loadCircleImage(url: String?, imageView: ImageView) {
    val myOptions: RequestOptions  =  RequestOptions().dontAnimate()
            .transform(GlideCircleTransform(imageView.context))
            .diskCacheStrategy(DiskCacheStrategy.ALL)

    Glide.with(imageView.context)
            .load(url)
            .apply(myOptions)
            .into(imageView)
}

这样就可以把一张图片显示到ImageView上,很简单吧。

Glide源码分析(主要流程)

从Glide用法中,我们知道,要显示一张图片,代码很简单,apply(myOptions)是设置一些额外功能,去掉也没问题。
下面是最简洁的用法:

Glide.with(context).load(url).into(imageView)

接下来,我们针对这句代码进行分析,虽然看似很简单,只有三句代码,但背后的源码实现极为复杂。所以此次分析,我们只看主要流程,不关注过多细节,因为一旦陷入细节,可能就无法把握主体流程了。
思路也很简单,我们就分析三个方法:with方法load方法into方法

1. Glide.with方法

我们进入Glide类,找到with方法:

@NonNull
public static RequestManager with(@NonNull Context context) {
  return getRetriever(context).get(context);
}

@NonNull
public static RequestManager with(@NonNull Activity activity) {
  return getRetriever(activity).get(activity);
}

@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
  return getRetriever(activity).get(activity);
}

@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
  return getRetriever(fragment.getActivity()).get(fragment);
}

@SuppressWarnings("deprecation")
@Deprecated
@NonNull
public static RequestManager with(@NonNull android.app.Fragment fragment) {
  return getRetriever(fragment.getActivity()).get(fragment);
}

@NonNull
public static RequestManager with(@NonNull View view) {
  return getRetriever(view.getContext()).get(view);
}

可以看到,with有很多重载方法,最后都返回一个RequestManager对象,也就是with方法会返回一个RequestManager对象
这些with重载方法分别接收参数Context、Activity、FragmentActivity、Fragment、android.app.Fragment、View。通过这些类型获取Context类型,然后进行下面操作:

  • 调用getRetriever方法:得到一个RequestManagerRetriever对象
  • 调用RequestManagerRetriever.get方法:得到RequestManager对象

接下来,分析这两个方法调用:
a. getRetriever方法

@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  Preconditions.checkNotNull(
      context,
      "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
          + "returns null (which usually occurs when getActivity() is called before the Fragment "
          + "is attached or after the Fragment is destroyed).");
  return Glide.get(context).getRequestManagerRetriever();
}

看最后一句代码,有两个方法调用:Glide.get()Glide.getRequestManagerRetriever(),我们先看Glide.get方法:

@NonNull
public static Glide get(@NonNull Context context) {
  if (glide == null) {
    synchronized (Glide.class) {
      if (glide == null) {
        checkAndInitializeGlide(context);
      }
    }
  }

  return glide;
}

Glide是采用单例模式,get方法用来获取实例,如果实例为空,则通过checkAndInitializeGlide方法初始化Glide实例:

private static void checkAndInitializeGlide(@NonNull Context context) {
  if (isInitializing) {
    throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
        + " use the provided Glide instance instead");
  }
  isInitializing = true;
  initializeGlide(context);
  isInitializing = false;
}

继续调用initializeGlide方法:

private static void initializeGlide(@NonNull Context context) {
  initializeGlide(context, new GlideBuilder());
}

private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
  Context applicationContext = context.getApplicationContext();
  //省略代码
  Glide glide = builder.build(applicationContext);
  //省略代码
  applicationContext.registerComponentCallbacks(glide);
  Glide.glide = glide;
}

通过GlideBuilder构建Glide对象,我们就直接看它的build方法:

@NonNull
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 =
        new Engine(
            memoryCache,
            diskCacheFactory,
            diskCacheExecutor,
            sourceExecutor,
            GlideExecutor.newUnlimitedSourceExecutor(),
            GlideExecutor.newAnimationExecutor(),
            isActiveResourceRetentionAllowed);
  }

  RequestManagerRetriever requestManagerRetriever =
      new RequestManagerRetriever(requestManagerFactory);

  return new Glide(
      context,
      engine,
      memoryCache,
      bitmapPool,
      arrayPool,
      requestManagerRetriever,
      connectivityMonitorFactory,
      logLevel,
      defaultRequestOptions.lock(),
      defaultTransitionOptions);
}

该方法中初始化了很多Glide成员变量,并且最后调用new Glide()构造了一个Glide对象。列举一下初始化的成员变量:

  • sourceExecutor:获取图片请求线程池GlideExecutor
  • diskCacheExecutor:从硬盘缓存加载图片线程池GlideExecutor
  • animationExecutor:执行动画的线程池GlideExecutor
  • memorySizeCalculator:从名字理解,是内存计算器
  • ...
  • memoryCache:内存缓存策略,LruResourceCache
  • diskCacheFactory:硬盘缓存工厂
  • engine:图片加载引擎
  • requestManagerRetriever:用来创建RequestManager的工具

初始化了Glide单例后,调用getRequestManagerRetriever方法获取RequestManagerRetriever对象:

@NonNull
public RequestManagerRetriever getRequestManagerRetriever() {
  return requestManagerRetriever;
}

此处的requestManagerRetriever就是在GlideBuilder.build方法中初始化的requestManagerRetriever。

b. 调用RequestManagerRetriever.get方法
找到RequestManagerRetriever中的get方法:

@NonNull
public RequestManager get(@NonNull Context context) {
  if (context == null) {
    throw new IllegalArgumentException("You cannot start a load on a null Context");
  } else 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) {
      return get(((ContextWrapper) context).getBaseContext());
    }
  }

  return getApplicationManager(context);
}

@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity);
    FragmentManager fm = activity.getSupportFragmentManager();
    return supportFragmentGet(
        activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

@NonNull
public RequestManager get(@NonNull Fragment fragment) {
  Preconditions.checkNotNull(fragment.getActivity(),
        "You cannot start a load on a fragment before it is attached or after it is destroyed");
  if (Util.isOnBackgroundThread()) {
    return get(fragment.getActivity().getApplicationContext());
  } else {
    FragmentManager fm = fragment.getChildFragmentManager();
    return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
  }
}

@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity);
    android.app.FragmentManager fm = activity.getFragmentManager();
    return fragmentGet(
        activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull View view) {
  if (Util.isOnBackgroundThread()) {
    return get(view.getContext().getApplicationContext());
  }

  Preconditions.checkNotNull(view);
  Preconditions.checkNotNull(view.getContext(),
      "Unable to obtain a request manager for a view without a Context");
  Activity activity = findActivity(view.getContext());
  if (activity == null) {
    return get(view.getContext().getApplicationContext());
  }

  if (activity instanceof FragmentActivity) {
    Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
    return fragment != null ? get(fragment) : get(activity);
  }

  android.app.Fragment fragment = findFragment(view, activity);
  if (fragment == null) {
    return get(activity);
  }
  return get(fragment);
}

我们发现,跟Glide.with方法类似,RequestManagerRetriever也有很多get重载方法,我们归类整理一下,实际可以分为两种类型:

  • Application生命周期RequestManager:get接收的参数类型是Application的Context 或者 调用get方法时处于子线程
  • Activity生命周期的RequestManager:get接收的参数是Activity、FragmentActivity、Fragment、View

先看怎么获取Application生命周期的RequestManager:

 @NonNull
 private RequestManager getApplicationManager(@NonNull Context context) {
   if (applicationManager == null) {
     synchronized (this) {
       if (applicationManager == null) {
         Glide glide = Glide.get(context.getApplicationContext());
         applicationManager =
             factory.build(
                 glide,
                 new ApplicationLifecycle(),
                 new EmptyRequestManagerTreeNode(),
                 context.getApplicationContext());
       }
     }
   }

   return applicationManager;
 }

在构建applicationManager时传入了一个ApplicationLifecycle对象,表明这个它的生命周期与应用生命周期相同。
接下来,分析如何获取Activity生命周期的RequestManager,这里涉及到两个方法:fragmentGet和supportFragmentGet

@SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"})
@Deprecated
@NonNull
private RequestManager fragmentGet(@NonNull Context context,
    @NonNull android.app.FragmentManager fm,
    @Nullable android.app.Fragment parentHint,
    boolean isParentVisible) {
  RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
  RequestManager requestManager = current.getRequestManager();
  if (requestManager == null) {
    Glide glide = Glide.get(context);
    requestManager =
        factory.build(
            glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
    current.setRequestManager(requestManager);
  }
  return requestManager;
}

@NonNull
private RequestManager supportFragmentGet(
    @NonNull Context context,
    @NonNull FragmentManager fm,
    @Nullable Fragment parentHint,
    boolean isParentVisible) {
  SupportRequestManagerFragment current =
      getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
  RequestManager requestManager = current.getRequestManager();
  if (requestManager == null) {
    Glide glide = Glide.get(context);
    requestManager =
        factory.build(
            glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
    current.setRequestManager(requestManager);
  }
  return requestManager;
}

两个方法中都是传入current.getGlideLifecycle(),这个方法返回一个ActivityFragmentLifecycle对象,代表Activity的生命周期。
以上介绍的两种生命周期区别如下:

  • Application生命周期:这种比较简单,即请求的生命周期与应用生命周期相同,当加载图片请求发出,只有退出应用,才能取消请求。
  • Activity生命周期:请求的生命周期与Activity相同,当请求发出,只要结束掉Activity,就会取消请求。那么怎样保证请求与Activity的生命周期相同呢?Glide源码中用了一个小技巧,即在每个Activity中都添加一个隐藏的Fragment,而Fragment的生命周期与Activity的生命周期一致,所以监听Fragment的生命周期就可以知道Activity的生命周期

到这里,Glide.with方法分析完毕,我们来做一个小结:Glide.with方法初始化了Glide单例,获取了一个RequestManager对象,并且根据传入参数类型的不同,RequestManager对请求生命周期的管理有两种方式,一种是与Application生命周期保持一致,另外一种是与Activity生命周期保持一致

2. RequestManager.load方法

分析完Glide.with方法后,再看一下RequestManager.load方法:

@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
  return asDrawable().load(string);
}

load也有很多重载方法,我们用的最多的是通过一个url地址加载图片,所以此处我们就分析入参是String类型的load方法。
这里有两个方法调用asDrawable()load(string),我们先看一下asDrawable:

@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
  return as(Drawable.class);
}

@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
    @NonNull Class<ResourceType> resourceClass) {
  return new RequestBuilder<>(glide, this, resourceClass, context);
}

创建了一个RequestBuilder对象,它的资源类型是Drawable。接着进入RequestBuilder类,分析load方法:

@NonNull
@CheckResult
@SuppressWarnings("unchecked")
@Override
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
  return loadGeneric(model);
}

@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
  this.model = model;
  isModelSet = true;
  return this;
}

做的事情很简答,就是初始化了RequestBuilder的成员变量modelisModelSet
到这里load方法分析完毕。小结一下:RequestManager.load方法创建并返回了一个RequestBuilder对象,该ReqeustBuilder指定的泛型是Drawable类型

3. RequestBuilder.into方法

最后,我们来分析一下into方法:

@NonNull
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
  Util.assertMainThread();
  Preconditions.checkNotNull(view);

  RequestOptions requestOptions = this.requestOptions;
  if (!requestOptions.isTransformationSet()
      && requestOptions.isTransformationAllowed()
      && view.getScaleType() != null) {
    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(
      glideContext.buildImageViewTarget(view, transcodeClass),
      /*targetListener=*/ null,
      requestOptions);
}

由于我们只关心主流程,所以直接看最后一句代码,通过buildImageViewTarget方法把ImageView转换成Target对象:

@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
    @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
  return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}

接着看ImageViewTargetFactory.buildTarget方法:

@NonNull
@SuppressWarnings("unchecked")
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)");
  }
}

由于Z是Drawable类型,所以把ImageView转换成了DrawableImageViewTarget对象(获取到图片后会通过该对象把图片设置给ImageView),接着就把这个Target作为参数调用into方法:

private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) {
  Util.assertMainThread();
  Preconditions.checkNotNull(target);
  if (!isModelSet) {
    throw new IllegalArgumentException("You must call #load() before calling #into()");
  }

  options = options.autoClone();
  //构建一个Request对象,从此处跟踪代码可知具体实现类是SingleRequest
  Request request = buildRequest(target, targetListener, options);

  Request previous = target.getRequest();
  if (request.isEquivalentTo(previous)
      && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
    request.recycle();
    if (!Preconditions.checkNotNull(previous).isRunning()) {
      previous.begin();
    }
    return target;
  }
  //如果target包含Request对象,则清空并且终止Request对象的执行
  requestManager.clear(target);
  //把新的Request对象绑定到target
  target.setRequest(request);
  //执行新的图片处理请求
  requestManager.track(target, request);

  return target;
}

在没有Glide的时候,我们处理RecyclerView或ListView图片加载是一件比较麻烦的事情,由于ListView中的Item的复用机制,会导致网络图片加载错位或闪烁。我们解决这个问题的办法也很简单,就是给当前ImageView设置tag,这个tag可以是图片的URL,当从网络获取图片时判断这个ImageView中的tag是否是这个图片的URL,如果是则直接显示图片,如果不是则从网络中加载图片,并且重新设置tag
有了Glide之后,我们处理ListView或RecyclerView中图片加载就很无脑了,根本不需要我们额外做操作,正常使用就行了。这其中的原理是Glide帮我们处理了这些判断,target.setRequest方法中,本质就是给ImageView设置tag,tag就是Request对象。而在设置新Request之前,会先判断target之前设置的Request和当前Request是否一致,如果一致则直接使用之前的Request。如果不一样,则通过requestManager.clear(target)清除tag,再通过target.setRequest重新设置新tag
接下来,我们看是怎样从网络加载图片的,跟踪requestManager.track这句代码:

void track(@NonNull Target<?> target, @NonNull Request request) {
   targetTracker.track(target);
   requestTracker.runRequest(request);
 }

继续看requestTracker.runReqeust方法:

public void runRequest(@NonNull Request request) {
  requests.add(request);
  if (!isPaused) {
    request.begin();
  } else {
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Paused, delaying request");
    }
    pendingRequests.add(request);
  }
}

这个方法用来执行Request请求,调用Request.begin,上面注释中提到Request的实现类是SingleRequest,所以我们看SingleRequest.begin方法:

@Override
public void begin() {
  assertNotCallingCallbacks();
  stateVerifier.throwIfRecycled();
  startTime = LogTime.getLogTime();
  if (model == null) {
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      width = overrideWidth;
      height = overrideHeight;
    }
    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");
  }
  if (status == Status.COMPLETE) {
    onResourceReady(resource, DataSource.MEMORY_CACHE);
    return;
  }
  status = Status.WAITING_FOR_SIZE;
  if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
    onSizeReady(overrideWidth, overrideHeight);
  } else {
    //最后也会调用onSizeReady方法
    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));
  }
}

首先,判断model是否为空,如果请求的url为空,那就不用其扭曲,直接调用onLoadFailed方法加载失败。
然后,调用onSizeReady方法,真正的资源加载就是从这个方法开始的。
根据status判断当前是否处于加载中间态和是否有占位图,如果有则设置加载过程中的占位图。
我们重点看onSizeReady方法:

@Override
public void onSizeReady(int width, int height) {
  stateVerifier.throwIfRecycled();
  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);
  if (status != Status.RUNNING) {
    loadStatus = null;
  }
}

先把status置为Running,然后获取图片的宽高属性值,接着通过Engine.load方法加载图片:

public <R> LoadStatus load(
      GlideContext glideContext,
      Object model,
      Key signature,
      int width,
      int height,
      Class<?> resourceClass,
      Class<R> transcodeClass,
      Priority priority,
      DiskCacheStrategy diskCacheStrategy,
      Map<Class<?>, Transformation<?>> transformations,
      boolean isTransformationRequired,
      boolean isScaleOnlyOrNoTransform,
      Options options,
      boolean isMemoryCacheable,
      boolean useUnlimitedSourceExecutorPool,
      boolean useAnimationPool,
      boolean onlyRetrieveFromCache,
      ResourceCallback cb) {
  Util.assertMainThread();
  long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

  EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
      resourceClass, transcodeClass, options);

  EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
  if (active != null) {
    cb.onResourceReady(active, DataSource.MEMORY_CACHE);
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from active resources", startTime, key);
    }
    return null;
  }

  EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
  if (cached != null) {
    cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from cache", startTime, key);
    }
    return null;
  }

  EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
  if (current != null) {
    current.addCallback(cb);
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Added to existing load", startTime, key);
    }
    return new LoadStatus(cb, current);
  }

  EngineJob<R> engineJob =
      engineJobFactory.build(
          key,
          isMemoryCacheable,
          useUnlimitedSourceExecutorPool,
          useAnimationPool,
          onlyRetrieveFromCache);

  DecodeJob<R> decodeJob =
      decodeJobFactory.build(
          glideContext,
          model,
          key,
          signature,
          width,
          height,
          resourceClass,
          transcodeClass,
          priority,
          diskCacheStrategy,
          transformations,
          isTransformationRequired,
          isScaleOnlyOrNoTransform,
          onlyRetrieveFromCache,
          options,
          engineJob);

  jobs.put(key, engineJob);

  engineJob.addCallback(cb);
  engineJob.start(decodeJob);

  if (VERBOSE_IS_LOGGABLE) {
    logWithTimeAndKey("Started new load", startTime, key);
  }
  return new LoadStatus(cb, engineJob);
}

重要的东西都在这里了:

  1. 判断是否在主线程,到目前为止一直在主线程运行,还没有开启子线程
  2. 创建一个EngineKey,key关联着model,即url。EngineKey可以用来指定缓存地址,可以从缓存中查找资源
  3. 根据key分别调用loadFromActiveResources和loadFromCache从内存缓存中查找资源,如果有,则直接回调cb.onResourceReady()来设置图片。
  4. 构建EngineJob和DecodeJob对象,这两个对象是加载资源的两个重要类,EngineJob负责开启线程去加载资源,获取资源后转换到主线程并进行回调。DecodeJob是真正的执行者,它是真正去网络加载资源的地方。EngineJob开启线程,执行DecodeJob,DecodeJob执行完之后叫EngineJob去分发回调,这就是二者的关系。

接下来,我们直接看加载图片的代码,EngineJob.start方法开启线程:

public void start(DecodeJob<R> decodeJob) {
  this.decodeJob = decodeJob;
  GlideExecutor executor = decodeJob.willDecodeFromCache()
      ? diskCacheExecutor
      : getActiveSourceExecutor();
  executor.execute(decodeJob);
}

很简单,冲线程池中分配一个线程执行DecodeJob(跟踪DecodeJob源码知道,DecodeJob实现了Runnable),到这里,从主线程切到子线程当中,接下来看run方法:

@Override
public void run() {
  GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
  DataFetcher<?> localFetcher = currentFetcher;
  try {
    if (isCancelled) {
      notifyFailed();
      return;
    }
    runWrapped();
  } catch (Throwable t) {
    if (stage != Stage.ENCODE) {
      throwables.add(t);
      notifyFailed();
    }
    if (!isCancelled) {
      throw t;
    }
  } finally {
    if (localFetcher != null) {
      localFetcher.cleanup();
    }
    GlideTrace.endSection();
  }
}

继续跟踪runWrapped方法:

private void runWrapped() {
  switch (runReason) {
    case INITIALIZE:
      stage = getNextStage(Stage.INITIALIZE);
      currentGenerator = getNextGenerator();
      runGenerators();
      break;
    case SWITCH_TO_SOURCE_SERVICE:
      runGenerators();
      break;
    case DECODE_DATA:
      decodeFromRetrievedData();
      break;
    default:
      throw new IllegalStateException("Unrecognized run reason: " + runReason);
  }
}

这个方法中涉及到从缓存中获取图片和从网络请求图片,暂时忽略缓存,我们直接看如何从往网络获取图片,直接跟进runGenerators方法:

private void runGenerators() {
  currentThread = Thread.currentThread();
  startFetchTime = LogTime.getLogTime();
  boolean isStarted = false;
  while (!isCancelled && currentGenerator != null
      && !(isStarted = currentGenerator.startNext())) {
    stage = getNextStage(stage);
    currentGenerator = getNextGenerator();

    if (stage == Stage.SOURCE) {
      reschedule();
      return;
    }
  }
  if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
    notifyFailed();
  }
}

在while循环中会调用currentGenerator.startNext方法,defaultGenerator是DataFetcherGenerator类型,它是一个接口,在getNextGenerator方法中初始化:

private DataFetcherGenerator getNextGenerator() {
  switch (stage) {
    case RESOURCE_CACHE:
      return new ResourceCacheGenerator(decodeHelper, this);
    case DATA_CACHE:
      return new DataCacheGenerator(decodeHelper, this);
    case SOURCE:
      return new SourceGenerator(decodeHelper, this);
    case FINISHED:
      return null;
    default:
      throw new IllegalStateException("Unrecognized stage: " + stage);
  }
}

由于我们只关注网络请求获取图片,所以直接看SourceGenerator这个实现类的startNext方法:

@Override
public boolean startNext() {
  if (dataToCache != null) {
    Object data = dataToCache;
    dataToCache = null;
    cacheData(data);
  }

  if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
    return true;
  }
  sourceCacheGenerator = null;

  loadData = null;
  boolean started = false;
  while (!started && hasNextModelLoader()) {
    loadData = helper.getLoadData().get(loadDataListIndex++);
    if (loadData != null
        && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
        || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
      started = true;
      loadData.fetcher.loadData(helper.getPriority(), this);
    }
  }
  return started;
}

这个方法中,我们着重看loadData.fetcher.loadData(helper.getPriority(), this)这段代码,Glide可以结合一些网络库一起使用,在项目中,我们是结合OkHttp使用的,所以这里的loader是OkHttpLoader类型,而fetcher则是OkHttpStreamFetcher类型,我们直接看它的loadData方法:

@Override
public void loadData(@NonNull Priority priority,
    @NonNull final DataCallback<? super InputStream> callback) {
  Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());
  for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
    String key = headerEntry.getKey();
    requestBuilder.addHeader(key, headerEntry.getValue());
  }
  Request request = requestBuilder.build();
  this.callback = callback;

  call = client.newCall(request);
  call.enqueue(this);
}

到这里,就很清楚了,完全是按OkHttp的四步走去发请求获取图片。图片获取成功之后,一步一步回调,回调步骤如下:
OkHttpStreamFetcher.onResponse() -> SourceGenerator.onDataReady() -> DecodeJob.onDataFetchReady() -> EngineJob.reschedule() ->
我们看一下reschedule方法:

@Override
public void reschedule(DecodeJob<?> job) {
  // Even if the job is cancelled here, it still needs to be scheduled so that it can clean itself
  // up.
  getActiveSourceExecutor().execute(job);
}

通过线程池执行DecodeJob,就会调用DecodeJob.run() -> DecodeJob.runWrapped(), 在runWrapped方法中,已经获取到了数据,状态会变成DECODE_DATA,接着调用decodeFromRetrievedData方法解析图片数据:

private void decodeFromRetrievedData() {
  if (Log.isLoggable(TAG, Log.VERBOSE)) {
    logWithTimeAndKey("Retrieved data", startFetchTime,
        "data: " + currentData
            + ", cache key: " + currentSourceKey
            + ", fetcher: " + currentFetcher);
  }
  Resource<R> resource = null;
  try {
    resource = decodeFromData(currentFetcher, currentData, currentDataSource);
  } catch (GlideException e) {
    e.setLoggingDetails(currentAttemptingKey, currentDataSource);
    throwables.add(e);
  }
  if (resource != null) {
    notifyEncodeAndRelease(resource, currentDataSource);
  } else {
    runGenerators();
  }
}

继续跟进decodeFromData方法:

private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
      DataSource dataSource) throws GlideException {
  try {
    if (data == null) {
      return null;
    }
    long startTime = LogTime.getLogTime();
    Resource<R> result = decodeFromFetcher(data, dataSource);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Decoded result " + result, startTime);
    }
    return result;
  } finally {
    fetcher.cleanup();
  }
}

接着看decodeFromFetcher方法:

@SuppressWarnings("unchecked")
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
    throws GlideException {
  LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
  return runLoadPath(data, dataSource, path);
}

看runLoadPath方法:

private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
      LoadPath<Data, ResourceType, R> path) throws GlideException {
  Options options = getOptionsWithHardwareConfig(dataSource);
  DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
  try {
    // ResourceType in DecodeCallback below is required for compilation to work with gradle.
    return path.load(
        rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
  } finally {
    rewinder.cleanup();
  }
}

返回path.load方法:

public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
      int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
  List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
  try {
    return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
  } finally {
    listPool.release(throwables);
  }
}

private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
    @NonNull Options options,
    int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
    List<Throwable> exceptions) throws GlideException {
  Resource<Transcode> result = null;
  for (int i = 0, size = decodePaths.size(); i < size; i++) {
    DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
    try {
      result = path.decode(rewinder, width, height, options, decodeCallback);
    } catch (GlideException e) {
      exceptions.add(e);
    }
    if (result != null) {
      break;
    }
  }
  if (result == null) {
    throw new GlideException(failureMessage, new ArrayList<>(exceptions));
  }

  return result;
}

这里又会调用DecodePath.decode方法:

public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
      @NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
  Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
  Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
  return transcoder.transcode(transformed, options);
}

transcoder是DrawableBytesTranscoder对象,所以看它的transcode方法:

@Nullable
@Override
public Resource<byte[]> transcode(@NonNull Resource<Drawable> toTranscode,
    @NonNull Options options) {
  Drawable drawable = toTranscode.get();
  if (drawable instanceof BitmapDrawable) {
    return bitmapBytesTranscoder.transcode(
        BitmapResource.obtain(((BitmapDrawable) drawable).getBitmap(), bitmapPool), options);
  } else if (drawable instanceof GifDrawable) {
    return gifDrawableBytesTranscoder.transcode(toGifDrawableResource(toTranscode), options);
  }
  return null;
}

然后进入BitmapDrawableTranscoder的transcode方法,这个方法返回一个Resource对象,接下来一层一层回调:
DecodeJob.notifyEncodeAndRelease() -> DecodeJob.notifyComplete -> EngineJob.onResourceReady -> MainThreadCallback.handleMessage -> SingleRequest.onResourceReady -> target.onResourceReady ->
直接看target实现类ImageViewTarget的onResourceReady方法:

@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
  if (transition == null || !transition.transition(resource, this)) {
    setResourceInternal(resource);
  } else {
    maybeUpdateAnimatable(resource);
  }
}

private void setResourceInternal(@Nullable Z resource) {
  setResource(resource);
  maybeUpdateAnimatable(resource);
}

再看实现类DrawableImageViewTarget的setResource方法:

@Override
protected void setResource(@Nullable Drawable resource) {
  view.setImageDrawable(resource);
}

到这里,总算把图片设置给了ImageView,至此,Glide加载图片的主体流程分析完毕了,可以看到这个过程很复杂,不是那么容易理解,好在总算把这个过程梳理完毕了。

Glide缓存机制

分析了Glide主体流程之后,我们来看一下Glide的缓存机制,从上面的源码分析,我们可以知道,Glide有内存缓存和硬盘缓存两种方式,接下来我们分别看这两种方式:

1. 内存缓存

Glide内存缓存分两种方式:若引用缓存LruCache缓存
下面看看怎样使用缓存,在Engine.load方法中有这样一段代码:

EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
        resourceClass, transcodeClass, options);

EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
  cb.onResourceReady(active, DataSource.MEMORY_CACHE);
  if (VERBOSE_IS_LOGGABLE) {
    logWithTimeAndKey("Loaded resource from active resources", startTime, key);
  }
  return null;
}

EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
  cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
  if (VERBOSE_IS_LOGGABLE) {
    logWithTimeAndKey("Loaded resource from cache", startTime, key);
  }
  return null;
}

源码分析时简单提了一句,但没做详细分析,这段代码就是从内存缓存中获取图片。逻辑如下:

  1. 先从弱引用缓存中获取图片,如果找到则直接回调
  2. 弱引用缓存没有找到图片,再从LruCache中取图片,如果找到图片则直接回调
  3. LruCache中也没有找到图片,再回走网络请求加载图片

这里着重说一下弱引用缓存,它用来保存正在使用的图片,下面代码中会有所介绍。
具体如何从缓存中获取的这里不做详细介绍,各位同学跟踪到loadFromActiveResouces方法和loadFromCache方法中可以看到。

那么,问题来了,又是在哪里把图片存储到内存缓存中呢?

通过网络加载图片完成之后,会调用EngineJob.handleResultOnMainThread方法:

@Synthetic
void handleResultOnMainThread() {
  stateVerifier.throwIfRecycled();
  if (isCancelled) {
    resource.recycle();
    release(false /*isRemovedFromQueue*/);
    return;
  } else if (cbs.isEmpty()) {
    throw new IllegalStateException("Received a resource without any callbacks to notify");
  } else if (hasResource) {
    throw new IllegalStateException("Already have resource");
  }
  engineResource = engineResourceFactory.build(resource, isCacheable);
  hasResource = true;

  //把正在使用的图片保存到弱引用缓存中,并且对使用数量+1
  engineResource.acquire();
  listener.onEngineJobComplete(this, key, engineResource);

  
  for (int i = 0, size = cbs.size(); i < size; i++) {
    ResourceCallback cb = cbs.get(i);
    if (!isInIgnoredCallbacks(cb)) {
      engineResource.acquire();
      cb.onResourceReady(engineResource, dataSource);
    }
  }
  //图片使用完成,对使用数量-1,当使用数量为0,则把它从弱引用缓存中移出,并且添加到LruCache缓存中
  engineResource.release();

  release(false /*isRemovedFromQueue*/);
}

这段代码涉及到两种缓存的使用,使用图片的过程中把图片存放在弱引用缓存,图片使用完成则存放在LruCache缓存。先看一下listener.onEngineJobComplete方法:

@SuppressWarnings("unchecked")
@Override
public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
  Util.assertMainThread();
  // A null resource indicates that the load failed, usually due to an exception.
  if (resource != null) {
    resource.setResourceListener(key, this);

    if (resource.isCacheable()) {
      activeResources.activate(key, resource);
    }
  }

  jobs.removeIfCurrent(key, engineJob);
}

跟踪到ActiveResources.activate方法:

void activate(Key key, EngineResource<?> resource) {
  ResourceWeakReference toPut =
      new ResourceWeakReference(
          key,
          resource,
          getReferenceQueue(),
          isActiveResourceRetentionAllowed);

  ResourceWeakReference removed = activeEngineResources.put(key, toPut);
  if (removed != null) {
    removed.reset();
  }
}

再看一下activeEngineResources的声明:

final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();

到这里就很明了,弱引用缓存使用一个HashMap来保存图片资源的弱引用,key则是Engine.load方法中创建的EngineKey对象。

接下来,继续上面提到的,分析LruCache缓存,当图片使用完毕之后,会调用EngineResource.release方法:

void release() {
  if (acquired <= 0) {
    throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
  }
  if (!Looper.getMainLooper().equals(Looper.myLooper())) {
    throw new IllegalThreadStateException("Must call release on the main thread");
  }
  if (--acquired == 0) {
    listener.onResourceReleased(key, this);
  }
}

此处的listener是Engine对象,所以我们看一下Engine.onResouceReleased方法:

@Override
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
  Util.assertMainThread();
  activeResources.deactivate(cacheKey);
  if (resource.isCacheable()) {
    cache.put(cacheKey, resource);
  } else {
    resourceRecycler.recycle(resource);
  }
}

一目了然,把图片资源存到cache中,并且从弱引用缓存中释放。此处的cache对象是LruResourceCache类型。

到这里,Glide内存缓存的两种方式:弱引用缓存LruCache缓存分析完毕了。

Glide默认开启内存缓存,我们可以通过RequestOptions.skipMemoryCache()方法设置是否使用内存缓存。

2. 硬盘缓存

Glide默认开启硬盘缓存,可以通过设置RequestOption.diskCacheStrategy(DiskCacheStrategy.NONE)来关闭硬盘缓存,硬盘缓存有几种策略:

  • DiskCacheStrategy.NONE:硬盘不缓存任何内容
  • DiskCacheStrategy.ALL:硬盘既缓存原始图片,也缓存转换之后的图片
  • DiskCacheStrategy.DATA:硬盘只缓存原始图片
  • DiskCacheStrategy.RESOURCE:硬盘只缓存转换之后的图片
  • DiskCacheStrategy.AUTOMATIC:Glide根据图片资源只能选择哪一种缓存策略(Glide默认选项)

解释一下原始图片转换后的图片,我们使用Glide加载图片的时候,Glide默认并不会展示原始图片,而是会对图片进行压缩和转换,经过着一系列的操作得到的图片,我们称之为转换后的图片。

对硬盘缓存有了清晰的认识之后,我们看一下Glide在哪里使用硬盘缓存,还记得DecodeJob中,我们提到过硬盘缓存吗。
我们再一起回顾一下DecodeJob中的getNextStage方法:

private Stage getNextStage(Stage current) {
  switch (current) {
    case INITIALIZE:
      return diskCacheStrategy.decodeCachedResource()
          ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
    case RESOURCE_CACHE:
      return diskCacheStrategy.decodeCachedData()
          ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
    case DATA_CACHE:
      // Skip loading from source if the user opted to only retrieve the resource from cache.
      return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
    case SOURCE:
    case FINISHED:
      return Stage.FINISHED;
    default:
      throw new IllegalArgumentException("Unrecognized stage: " + current);
  }
}

这个方法逻辑比较清晰:

  1. 判断当次请求支持使用硬盘缓存的转换后图片。如果是的则stage = Stage.RESOURCE_CACHE
  2. 判断当次请求支持使用硬盘缓存的原始图片。如果是的则stage = Stage.DATA_CACHE
  3. 如果上面两种方式都不支持,则根据是否只能通过硬盘缓存获取图片把stage = Stage.SOURCE或者stage = Stage.FINISHED

那么这个stage有什么用呢?

别着急,我们继续看DecodeJob.getNextGenerator方法:

private DataFetcherGenerator getNextGenerator() {
  switch (stage) {
    case RESOURCE_CACHE:
      return new ResourceCacheGenerator(decodeHelper, this);
    case DATA_CACHE:
      return new DataCacheGenerator(decodeHelper, this);
    case SOURCE:
      return new SourceGenerator(decodeHelper, this);
    case FINISHED:
      return null;
    default:
      throw new IllegalStateException("Unrecognized stage: " + stage);
  }
}

不同的stage会对应不同的DataFetcherGenerator,源码分析中,我们只分析了SourceGenerator,而除此之外,这里还有ResourceCacheGenerator和DataCacheGenerator,这两个类分别对应从硬盘获取转换后图片资源 和 从硬盘获取原始图片资源。获取过程和从网络获取类似,也是从从线程池中分配一个线程,从硬盘获取图片资源并且进行解码操作,然后把图片设置个ImageView

知道了如何使用硬盘缓存,那么还有一个问题,Glide是什么时候把图片存到硬盘上的呢?

带着这个疑问,我们继续看源码,在decodeFromRetrievedData方法中,会调用notifyEncodeAndRelease方法:

private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
  if (resource instanceof Initializable) {
    ((Initializable) resource).initialize();
  }

  Resource<R> result = resource;
  LockedResource<R> lockedResource = null;
  if (deferredEncodeManager.hasResourceToEncode()) {
    lockedResource = LockedResource.obtain(resource);
    result = lockedResource;
  }

  notifyComplete(result, dataSource);

  stage = Stage.ENCODE;
  try {
    if (deferredEncodeManager.hasResourceToEncode()) {
      deferredEncodeManager.encode(diskCacheProvider, options);
    }
  } finally {
    if (lockedResource != null) {
      lockedResource.unlock();
    }
  }
  // Call onEncodeComplete outside the finally block so that it's not called if the encode process
  // throws.
  onEncodeComplete();
}

在这个方法中调用了DeferredEncodeManager.encode方法:

void encode(DiskCacheProvider diskCacheProvider, Options options) {
  GlideTrace.beginSection("DecodeJob.encode");
  try {
    diskCacheProvider.getDiskCache().put(key,
        new DataCacheWriter<>(encoder, toEncode, options));
  } finally {
    toEncode.unlock();
    GlideTrace.endSection();
  }
}

这里就很明显了,把图片资源存到硬盘上。

到这里,Glide两种缓存方式就分析完毕了,可以说Glide的缓存策略非常完善,只要掌握了这些缓存策略,我们可以根据项目中的实际情况灵活调整缓存方式。

Glide图变换

我们可以通过RequestOption设置图片的变换,包括center_crop、center_inside、circle_crop...,那么这些变换是在哪里对图片起作用的呢?

举个例子,我想显示一个圆形的图片,那么就可以设置:

RequestOptions options = new RequestOptions().circleCrop();

我们跟进circleCrop方法:

@NonNull
@CheckResult
public RequestOptions circleCrop() {
  return transform(DownsampleStrategy.CENTER_INSIDE, new CircleCrop());
}

可以看到,这里创建了一个CircleCrop对象,我们看一下CircleCrop的实现:

public class CircleCrop extends BitmapTransformation {
  private static final int VERSION = 1;
  private static final String ID = "com.bumptech.glide.load.resource.bitmap.CircleCrop." + VERSION;
  private static final byte[] ID_BYTES = ID.getBytes(CHARSET);

  @SuppressWarnings("PMD.CompareObjectsWithEquals")
  @Override
  protected Bitmap transform(
      @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
    return TransformationUtils.circleCrop(pool, toTransform, outWidth, outHeight);
  }

  @Override
  public boolean equals(Object o) {
    return o instanceof CircleCrop;
  }

  @Override
  public int hashCode() {
    return ID.hashCode();
  }

  @Override
  public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
    messageDigest.update(ID_BYTES);
  }
}

这个类继承自BitmapTransformation类,主要的转换过程在transform方法中,会调用TransformationUtils.circleCrop方法:

public static Bitmap circleCrop(@NonNull BitmapPool pool, @NonNull Bitmap inBitmap,
      int destWidth, int destHeight) {
  int destMinEdge = Math.min(destWidth, destHeight);
  float radius = destMinEdge / 2f;

  int srcWidth = inBitmap.getWidth();
  int srcHeight = inBitmap.getHeight();

  float scaleX = destMinEdge / (float) srcWidth;
  float scaleY = destMinEdge / (float) srcHeight;
  float maxScale = Math.max(scaleX, scaleY);

  float scaledWidth = maxScale * srcWidth;
  float scaledHeight = maxScale * srcHeight;
  float left = (destMinEdge - scaledWidth) / 2f;
  float top = (destMinEdge - scaledHeight) / 2f;

  RectF destRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);

  // Alpha is required for this transformation.
  Bitmap toTransform = getAlphaSafeBitmap(pool, inBitmap);

  Bitmap.Config outConfig = getAlphaSafeConfig(inBitmap);
  Bitmap result = pool.get(destMinEdge, destMinEdge, outConfig);
  result.setHasAlpha(true);

  BITMAP_DRAWABLE_LOCK.lock();
  try {
    Canvas canvas = new Canvas(result);
    // Draw a circle
    canvas.drawCircle(radius, radius, radius, CIRCLE_CROP_SHAPE_PAINT);
    // Draw the bitmap in the circle
    canvas.drawBitmap(toTransform, null, destRect, CIRCLE_CROP_BITMAP_PAINT);
    clear(canvas);
  } finally {
    BITMAP_DRAWABLE_LOCK.unlock();
  }

  if (!toTransform.equals(inBitmap)) {
    pool.put(toTransform);
  }

  return result;
}

这个方法是真正的转换过程,计算图片的宽高,重新生成新的Bitmap。

系统提供了几种常用的图片变换成方式,我们也可以自定义变换,各位可以根据项目要求实现自定义变换。

到这里,图片变换也介绍完毕了。

对比Glide、Picasso、Fresco

image.png

可以看到,三大主流图片加载框架性能都不错,不过整体而言Glide更胜一筹。

总结

Glide确实是一款强大易用的图片加载框架,不过背后,是框架为我们做了很多的工作。
写这篇文章历经了很长时间,主要是因为Glide源码部分太过复杂。分析了Glide之后,Picasso和Fresco就不再做分析,原理大同小异,各位同学感兴趣的话可以自己去分析那两个框架。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容