Android Glide 源码分析和学习

1.引言

最近在开发产品过程中,项目经理提了一个问题:"为什么图片加载这么缓慢?",我看了看的确非常缓慢,图片加载用的经典的Glide框架,慢难道仅仅只是网络的原因?带着这份不解,我下了一个glide源码,开启了学习之路。

2.正题

通过学习想彻底弄懂这几个问题:

  1. Glide加载流程
  2. Glide切换Activity,是否有中断和恢复操作
  3. Glide的内存管理
  4. Glide的弱网管理机制

2.1 Glide总体加载流程

未命名文件.jpg

Glide框架就像是一条流水线,每个环节做什么,每个环节为下一步输出什么。都非常清楚明了。把握其中的主要流程和环节,那么搞懂全貌只是时间问题。

接下来我们以下面的代码为基础开始正式分析我们的glide源码加载流程:

Glide.with(Context context)
         .load(Strint url)
         .into(ImageView imageView);

2.1.1 Request产生阶段

Glide类:

Glide类和OkhttpClient一样,首先都是一个单例,目的是为整个框架做初始化以及为使用者提供唯一的使用窗口:

Glide.with 接受如下几个参数:

  • @see #with(android.app.Activity)
  • @see #with(android.app.Fragment)
  • @see #with(androidx.fragment.app.Fragment)
  • @see #with(androidx.fragment.app.FragmentActivity)

不同的参数,所代表的lifecycle也就不同。这样在生命周期处理这块就有所不同。with()方法最终会调用RequestManagerRetriever的get方法得到一个RequestManager

RequestManagerRetriever类:

RequestManagerRetriever类中是直接生产RequestManager的,生产主要是靠RequestManagerFactory。其次调用checkAndInitializeGlide()来初始化Glide

GlideBuilder类:

checkAndInitializeGlide()方法最终会调用到GlideBuilder.build. 对整个Glide框架中的GlideExecutor,加载引擎Engine,DiskCache 等做初始化。一个非常典型的建造者模式使用场景

RequestManager类:

RequestManager以Manager,肯定是包含了:

  • 请求的创建,setRequestOptions
  • 请求的管理队列 移除、等待
  • 请求开始、请求暂停、取消、重新开始,设置RequestListener

请求的暂停、取消等是通过:RequestTracker类去跟踪处理
请求的创建以及各种配置是通过:RequestBuilder类完成

RequestBuilder类:
为Request设置完毕RequestOptions之后就会开始,进行build得到真正的Request对象。这一步骤是通过调用into(Target t)方法。

private > Y into(
      @NonNull Y target,
      @Nullable RequestListener targetListener,
      BaseRequestOptions options,
      Executor callbackExecutor) {
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    Request request = buildRequest(target, targetListener, options, callbackExecutor);//

    Request previous = target.getRequest();//判断此请求是否被加载过
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      // If the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
        // that are done in the individual Request.
        previous.begin();//假如Request存在的话 就直接走begin开始走Engin流程
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);//开启网络请求

    return target;
  }

这一步骤应该有俩个疑问:

  • 传入的ImageView如何转换成Target的

  • buildRequest方法中是如何产生一个Request的

解答1:

load(ImageView )方法最终会调用GlideContext.buildImageViewTarget 方法转换得到Target。源码如下:

public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public  ViewTarget buildTarget(
      @NonNull ImageView view, @NonNull Class clazz) {
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }
}

解答2:
Request是通过GlideContext.buildRequestRecursive 产生的。Request是一个抽象类:具体的实现有这几个:

image.png

根据配置不同生成的Request就不同。一般网络请求产生的是SingleRequest; 到这里 前期工作做好了,Request也产生了。于是开始交给Engine引擎去深层次加工。

小结:Glide前期代码执行流程:

image.png

2.1.2 加载Request

上面得到Request之后,会调用RequestManager.track()开启 加载。RequestManager中对请求的管理。最终都是通过RequestTracker实现。

RequestTracker类:
作用:开始、暂停、取消请求

RunRequest方法 代码如下:

/** Starts tracking the given request. */
  public void runRequest(@NonNull Request request) {
    requests.add(request);//添加Request到set中
    if (!isPaused) {
      request.begin();//开启真正的请求
    } else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);
    }
  }

SingleRequest类:
由上面我们知道了,产生的request是SingleRequest.我们来看下SingleRequest是干什么的。

image.png

每个SingleRequest都有这6中状态。SingleRequest类有一个Status变量,用来标记当前SingleRequst的状态。

SingleRequest#begin方法如下:

@Override
  public void begin() {
    synchronized (requestLock) {
     
      //加载完成从内存中去取
      if (status == Status.COMPLETE) {
        onResourceReady(
            resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);        return;
      }

      // Restarts for requests that are neither complete nor running can be treated as new requests
      // and can run again from the beginning.

      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));
      }
    }
  }

onSizeReady() 调用了Engine.load方法正式开始加载。是加载的入口

      loadStatus =
          engine.load(
              glideContext,
              model,
              requestOptions.getSignature(),
              this.width,
              this.height,
              requestOptions.getResourceClass(),
              transcodeClass,
              priority,
              requestOptions.getDiskCacheStrategy(),
              requestOptions.getTransformations(),
              requestOptions.isTransformationRequired(),
              requestOptions.isScaleOnlyOrNoTransform(),
              requestOptions.getOptions(),
              requestOptions.isMemoryCacheable(),
              requestOptions.getUseUnlimitedSourceGeneratorsPool(),
              requestOptions.getUseAnimationPool(),
              requestOptions.getOnlyRetrieveFromCache(),
              this,
              callbackExecutor);


Engine类:
engine是整个Glide的灵魂所在,著名的缓存机制都是在Engine中进行的。和我们之前学习一样。Engine肯定又是一个“大家长”。其主要功能是负责开启load Request 且管理资源的缓存。这些功能都是由其内部一个又一个类构成。往往在这个类中就会声明实现各个模块的“小部件”


public class Engine
    implements EngineJobListener,
        MemoryCache.ResourceRemovedListener,
        EngineResource.ResourceListener {
  private static final String TAG = "Engine";
  private static final int JOB_POOL_SIZE = 150;
  private static final boolean VERBOSE_IS_LOGGABLE = Log.isLoggable(TAG, Log.VERBOSE);
  private final Jobs jobs;//通过HashMap记录产生的EngineJob
  private final EngineKeyFactory keyFactory;
  private final MemoryCache cache;//内存缓存机制第二层
  private final EngineJobFactory engineJobFactory;//生产EngineJob
  private final ResourceRecycler resourceRecycler;
  private final LazyDiskCacheProvider diskCacheProvider;
  private final DecodeJobFactory decodeJobFactory;
  private final ActiveResources activeResources;//内存缓存机制第一层

Engine#load:

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

    //标记EngineResource的独一无二的key
    EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);

    EngineResource memoryResource;
    synchronized (this) {
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); //缓存机制1:从内存加载
      if (memoryResource == null) {
        return waitForExistingOrStartNewJob(
            glideContext,
            model,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            options,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache,
            cb,
            callbackExecutor,
            key,
            startTime);
      }
    }

loadFromMemory方法:就是Glide框架中的第一层缓存机制---内存缓存。

@Nullable
  private EngineResource loadFromMemory(
      EngineKey key, boolean isMemoryCacheable, long startTime) {
    if (!isMemoryCacheable) {
      return null;
    }

    EngineResource active = loadFromActiveResources(key);
    if (active != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return active;
    }

    EngineResource cached = loadFromCache(key);
    if (cached != null) {
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return cached;
    }

    return null;
  }

我们首先看看第一层内存缓存 loadFromActiveResources:

  @Nullable
  private EngineResource loadFromActiveResources(Key key) {
    EngineResource active = activeResources.get(key);
    if (active != null) {
      active.acquire();
    }

    return active;
  }

ActiveResources类:

final class ActiveResources {
  private final boolean isActiveResourceRetentionAllowed;
  private final Executor monitorClearedResourcesExecutor;
  @VisibleForTesting final Map activeEngineResources = new HashMap<>();//缓存EngineResource。
  private final ReferenceQueue> resourceReferenceQueue = new ReferenceQueue<>();

  private ResourceListener listener;

ResourceWeakReference 对象可以理解成是对EngineResource包装。本身是弱引用。当前程序中被使用的EngineResource都会放在这个map中。这样当在其他地方再次使用,可以直接从内存中查找出来,根据EngineKey。

active.acquire();采用了引用计数机制(类似jvm内存管理中的),被引用一次就+1,当为0的时候,就会退居二线内存容器中,也就是会被添加到MemoryCache中。

我们首先看看第二层内存缓存loadFromCache:

  private EngineResource loadFromCache(Key key) {
    EngineResource cached = getEngineResourceFromCache(key);
    if (cached != null) {
      cached.acquire();
      activeResources.activate(key, cached);
    }
    return cached;
  }

private EngineResource getEngineResourceFromCache(Key key) {
    Resource cached = cache.remove(key);

    final EngineResource result;
    if (cached == null) {
      result = null;
    } else if (cached instanceof EngineResource) {
      // Save an object allocation if we've cached an EngineResource (the typical case).
      result = (EngineResource) cached;
    } else {
      result =
          new EngineResource<>(
              cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
    }
    return result;
  }

cache指的就是MemoryCache.其实现类是:LruResourceCache 类继承自 LruCache。内部主要是维护了一个LinkedHashMap。

关于Lru算法可以参考这个文章:Lru算法
LinkHashMap 参考这个:LinkHashMap介绍

GetEngineResourceFromCache 就是从第二层LruCache中查找EngineResource。上面是调用remove方法。意味着当命中EngineKey。LruCache会删除这个EngineKey。同时activeResources调用put方法。放进一级缓存中。同时计数+1

流程图:


image.png

内存缓存的机制就讲到这里。因为第一次加载网络图片,内存中肯定是找不到EngineResource的。 找不到怎么办呢? 会调用waitForExistingOrStartNewJob 方法进而产生一个EngineJob和DecodeJob

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

    DecodeJob 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, callbackExecutor);
    engineJob.start(decodeJob);

EngineJob类:

EngineJob是Engine处理每个请求的最小单元,里面有load success/fail 等回调;也有开启load,取消load等操作

image.png

DecodeJob类:

DecodeJob 负责从diskCache中或者服务器上解码得到图片的数据。实现了Runnable接口。是真正进行请求的核心类;也是文件缓存的入口。

EngineJob调用Start方法治好,会执行DecodeJob run方法。run代码如下:

@SuppressWarnings("PMD.AvoidRethrowingException")
  @Override
  public void run() {
    GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
    DataFetcher localFetcher = currentFetcher;
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();//关键
    } catch (CallbackException e) {
      throw e;
    } catch (Throwable t) {
  }

  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);
    }
  }

  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);
    }
  }

DataFetcherGenerator是一个接口,实现类如下:

image.png
万众瞩目的文件缓存来!!!

runWrapped() 调用顺序

  • 调用ResourceCacheGenerator.startNext方法 从文件中加载数据
  • 找不到数据,调用DataCacheGenerator的startNext 从另外的文件中查询数据;
  • 依然查询不到就调用SourceGenerator.startNext从网络上加载数据。

ResourceCacheGenerator.startNext方法如下:

 currentKey =
          new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
              helper.getArrayPool(),
              sourceId,
              helper.getSignature(),
              helper.getWidth(),
              helper.getHeight(),
              transformation,
              resourceClass,
              helper.getOptions());
      cacheFile = helper.getDiskCache().get(currentKey);
      if (cacheFile != null) {
        sourceKey = sourceId;
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);
      loadData =
          modelLoader.buildLoadData(
              cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
      if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
        started = true;
        loadData.fetcher.startNext(helper.getPriority(), this);
      }
    }

cacheFile = helper.getDiskCache().get(currentKey);
从文件去寻找图片资源

找到之后,调用去读取数据

loadData.fetcher.startNext(helper.getPriority(), this);

DataCacheGenerator的流程和ResourceCacheGenerator流程几乎一样。都是从文件中加载图片区别在于:

  • ResourceCacheGenerator文件中保存的是经过转换的图片,例如转换成圆角。那张圆角就是保存在ResourceCacheGenerator 中

  • DataCacheGenerator 是保存最原始的,也就是从服务器拉下来的那张图片。

当然这也与Glide 缓存策略有关:

ALL:既缓存原始图片,也缓存转换过后的图片;对于远程图片,缓存 DATA 和 RESOURCE;对于本地图片,只缓存 RESOURCE。

AUTOMATIC (默认策略):尝试对本地和远程图片使用最佳的策略。当你加载远程数据(比如,从 URL 下载)时,AUTOMATIC 策略仅会存储未被你的加载过程修改过 (比如,变换、裁剪等) 的原始数据(DATA),因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,AUTOMATIC 策略则会仅存储变换过的缩略图(RESOURCE),因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。

DATA:只缓存未被处理的文件。我的理解就是我们获得的 stream。它是不会被展示出来的,需要经过装载 decode,对图片进行压缩和转换,等等操作,得到最终的图片才能被展示。

NONE:表示不缓存任何内容。

RESOURCE:表示只缓存转换过后的图片(也就是经过decode,转化裁剪的图片)。

完整的内存缓存图:

image.png

DataFetcherGenerator 加载数据最终都是靠DataFetcher来实现的。DataFetcher接口。常用的DataFetch实现由如下三种:

image.png

HttpUrlFetcher:默认的从服务器拉取数据的实现。SourceGenerator 持有的DataFetcher

FileFetcher:从文件中读取数据。ResourceCacheGenerator持有的。

接下来看看 HttpUrlFetcher#loadData

@Override
  public void loadData(
      @NonNull Priority priority, @NonNull DataCallback callback) {
    long startTime = LogTime.getLogTime();
    try {
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      callback.onDataReady(result);
    } catch (IOException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Failed to load data for url", e);
      }
      callback.onLoadFailed(e);
    } finally {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }

loadDataWithRedirects方法就不展开讲了,内部就是通过网络请求最终返回一个InputStream.

到此整个请求的数据就拿到了。Glide的缓存机制也分析了一遍。接下来就是各种回调。

2.1.3 请求回调处理

HttpUrlFetcher#loadData中的 callback.onDataReady(result);经过层层的回调一直到Engine类中。流程图如下:

image.png

以上就是整个Glide加载流程的主脉络。很多细节问题没有去深究。等以后遇到问题再去弄懂细节问题。

这篇文章整整花了4个小时时间抒写。回调章节讲的也是很简单。可以打印下堆栈快速弄懂回调逻辑。

你可能感兴趣的:(Android Glide 源码分析和学习)