阅读 1735

Android源码系列-解密Glide

Glide是什么?

Glide是一个Android的图片加载和缓存库,它主要专注于大量图片的流畅加载。是google所推荐的图片加载库,作者是bumptech。这个库被广泛运用在google的开源项目中,包括2014年的google I/O大会上发布的官方App。

简介

WIKI地址:WIKI官网

Github地址:Github

特点

1、多样化媒体加载

Glide 不仅是一个图片缓存,它支持 Gif、WebP等格式

2、生命周期集成

我们可以更加高效的使用Glide提供的方式进行绑定,这样可以更好的让加载图片的请求的生命周期动态管理起来

3、高效的缓存策略

  • 支持Memory和Disk图片缓存
  • 根据 ImageView 的大小来缓存相应大小的图片尺寸
  • 内存开销小,默认的 Bitmap 格式是 RGB_565 格式(3.X版本),4.7.1版本默认格式为(PREFER_ARGB_8888_DISALLOW_HARDWARE)
  • 使用BitmapPool进行Bitmap的复用

4、 提供丰富的图片转换Api,支持圆形裁剪、平滑显示等特性

Glide怎么用?

1、gradle引入库,implementation 'com.github.bumptech.glide:glide:4.7.1'

2、配置Glide的with load apply into等方法

 public void loadImageView(ImageView view,String url){
       //属性的配置
       RequestOptions options = new RequestOptions()
               //加载成功之前占位图
               .placeholder(R.mipmap.ic_launcher)
               //加载错误之后的错误图
               .error(R.mipmap.ic_launcher)
               //指定图片的尺寸
               .override(1000,800)
               //指定图片的缩放类型为fitCenter (等比例缩放图片,宽或者是高等于ImageView的宽或者是高。)
               .fitCenter()
               //指定图片的缩放类型为centerCrop (等比例缩放图片,直到图片的狂高都大于等于ImageView的宽度,然后截取中间的显示。)
               .centerCrop()
               .circleCrop()//指定图片的缩放类型为centerCrop (圆形)
               //跳过内存缓存
               .skipMemoryCache(true)
               //缓存所有版本的图像
               .diskCacheStrategy(DiskCacheStrategy.ALL)
               //跳过磁盘缓存
               .diskCacheStrategy(DiskCacheStrategy.NONE)
               //只缓存原来分辨率的图片
               .diskCacheStrategy(DiskCacheStrategy.DATA)
               //只缓存最终的图片
               .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
               .priority(Priority.HIGH)
               ;
       //加载图片
       Glide.with(getApplicationContext())
               .load(url)
               .apply(options)
               .into(view);
   }
   
复制代码

3、执行ImageView的加载

 loadImageView(ivPic,"http://b.hiphotos.baidu.com/image/pic/item/d52a2834349b033bda94010519ce36d3d439bdd5.jpg");
 
复制代码

详细的使用教程及option的配置,推荐参考

Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

Glide核心执行流程是怎样?

基础概念

类型 说明
Data 代表原始的,未修改过的资源,对应dataClass
Resource 修改过的资源,对应resourceClass
Transcoder 资源转换器,比如BitmapBytesTranscoder(Bitmap转换为Bytes),GifDrawableBytesTranscoder
ResourceEncoder 持久化数据的接口,注意,该类并不与decoder相对应,而是用于本地缓存的接口
ResourceDecoder 数据解码器,比如ByteBufferGifDecoder(将ByteBuffer转换为Gif),StreamBitmapDecoder(Stream转换为Bitmap)
ResourceTranscoder 资源转换器,将给定的资源类型,转换为另一种资源类型,比如将Bitmap转换为Drawable,Bitmap转换为Bytes
Transformation 比如对图片进行FitCenter,CircleCrop,CenterCrop的transformation,或者根据给定宽高对Bitmap进行处理的BitmapDrawableTransformation
Target request的载体,各种资源对应的加载类,含有生命周期的回调方法,方便开发人员进行相应的准备以及资源回收工作

总体设计

image

1、构建Request,实现类为SingleRequest,用于发起一个加载的请求

2、通过EngineJob和DecodeJob负责任务创建,发起,回调,资源的管理

3、根据请求的资源类型,最后匹配对应的DateFetcher进行Data数据的获取

4、获取数据进行相应的缓存配置

5、根据原始数据Data进行解码及转换,生成最终需要显示的Resource

6、通过回调Target对应的方法,最后进行图片的显示

关键类功能说明

功能说明
Glide 向外暴露单例静态接口,构建Request,配置资源类型,缓存策略,图片处理等,可以直接通过该类完成简单的图片请求和填充。内部持有一些内存变量BitmapPool,MemoryCache,ByteArrayPool,便于低内存情况时自动清理内存
RequestManagerRetriever 用于创建RequestManager对象,并与Context做相应的生命周期绑定
RequestManagerFragment Glide向Activity或Fragment中添加的空Fragment,用于控制绑定生命周期
LifecycleListener 用于监听Activity或者Fragment的生命周期方法的接口
RequestManager 用户管理及发起请求,支持resume、pause、clear等操作
RequestBuilder 创建请求,资源类型配置,缩略图配置,以及通过BaseRequestOptions进行一些默认图,图片处理的配置
Engine 任务创建,发起,回调,管理存活和缓存的资源
EngineJob 调度DecodeJob,添加,移除资源回调,并notify回调
DecodeJob 实现了Runnable接口,调度任务的核心类,整个请求的繁重工作都在这里完成:处理来自缓存或者原始的资源,应用转换动画以及transcode。负责根据缓存类型获取不同的Generator加载数据,数据加载成功后回调DecodeJob的onDataFetcherReady方法对资源进行处理
ResourceCacheGenerator 尝试从修改过的资源缓存中获取,如果缓存未命中,尝试从DATA_CACHE中获取
DataCacheGenerator 尝试从未修改过的本地缓存中获取数据,如果缓存未命中则尝试从SourceGenerator中获取
SourceGenerator 从原始的资源中获取,可能是服务器,也可能是本地的一些原始资源
DataFetcher 数据加载接口,通过loadData加载数据并执行对应的回调
LoadPath 根据给定的数据类型的DataFetcher尝试获取数据,然后尝试通过一个或多个decodePath进行decode
DecodePath 根据指定的数据类型对resource进行decode和transcode
Registry 管理组件(数据类型+数据处理)的注册
ModelLoaderRegistry 注册所有数据加载的loader
ResourceDecoderRegistry 注册所有资源转换的decoder
TranscoderRegistry 注册所有对decoder之后进行特殊处理的transcoder
ResourceEncoderRegistry 注册所有持久化resource(处理过的资源)数据的encoder
EncoderRegistry 注册所有的持久化原始数据的encoder

代码执行流程

先贴一下流程图,建议通过源码结合流程图进行分析,下面再分步骤进行分析。

image

下面主要从with()、load()、into()3个方法进行分析。

with()

1、with()方法,最后会返回一个RequestManger对象用于发起Request。

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

复制代码

2、getRetriever(context),最后返回一个RequestManagerRetriever对象用于生成RequestManager。

 private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    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(contenxt),会对glide进行初始化,模块扫描、组件注册等工作。

3、RequestManagerRetriever的get(context)方法

 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);
  }
复制代码

这里主要根据context的类型,去创建不同的RequestManager的对象,绑定生命周期。 如果context是Application对象的话,则调用,绑定了ApplicationLifecycle。

 private RequestManager getApplicationManager(@NonNull Context context) {
    // Either an application context or we're on a background thread.
    if (applicationManager == null) {
      synchronized (this) {
        if (applicationManager == null) {
          // Normally pause/resume is taken care of by the fragment we add to the fragment or
          // activity. However, in this case since the manager attached to the application will not
          // receive lifecycle events, we must force the manager to start resumed using
          // ApplicationLifecycle.

          // TODO(b/27524013): Factor out this Glide.get() call.
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }
复制代码

4、如果context是Activity或Fragment的话,则会调用supportFragmentGet、FragmentGetd方法,创建RequestManagerFragment进行生命周期绑定ActivityFragmentLifecycle。

  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) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

复制代码

总结一下:with()方法,最后会返回一个根据context类型绑定生命周期的RequestManger对象。

load()

1.load()方法最后会生成一个RequestBuilder对象,用于构建Request,并执行请求操作.。首先会通过as方法生成一个RequestBuilder对象

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

2、执行load()方法

  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
复制代码

3、执行loadGeneric方法

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

保存load传进行的参数,并设置isModelSet为true

4、设置请求的配置参数,apply(RequestOptions requestOptions)

  public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
    Preconditions.checkNotNull(requestOptions);
    this.requestOptions = getMutableOptions().apply(requestOptions);
    return this;
  }
复制代码

总结一下load()方法,最后会返回一个RequestBuilder对象,通过apply设置请求的参数,用于构建Request。

into()

into()是整个过程最复杂的一步,简单来说就是通过缓存策略及注册的Moderload,最终去加载源数据Data,并进行转换为配置的Resource,最后显示再Target上。

1、RequestBuilder的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 request = buildRequest(target, targetListener, options);

    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      // 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();
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }
复制代码

2、会执行BuildRequest()生成Request对象。经过一系列的调用,最后执行的方法如下,会返回一个SingleRequest对象实例

 private Request obtainRequest(
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      RequestOptions requestOptions,
      RequestCoordinator requestCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight) {
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListener,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory());
  }
复制代码

3、接着会调用 requestManager.track(target, request);实现如下:

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

4、这里涉及到一个新类RequestTracker,用于管理Request的生命周期。runRequest实现如下:

 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);
    }
  }
复制代码

5、最后会调用Request的begin()方法开始执行请求,实现如下:

 public void begin() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    if (model == null) {
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        width = overrideWidth;
        height = overrideHeight;
      }
      // Only log at more verbose log levels if the user has set a fallback drawable, because
      // fallback Drawables indicate the user expects null models occasionally.
      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;
    }

    // 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));
    }
  }
复制代码

这里主要会根据status进行判断,如果COMPLETE已完成,则直接回调 onResourceReady,如果是WAITING_FOR_SIZE,则会执行onSizeReady方法,我们都知道Glide会根据实际显示的View宽高去生成最后的Resource进行显示。

6、onResourceReady实现如下:

  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));
    }
  }
复制代码

最终会通过engine的load方法去执行请求,后续的缓存策略、数据加载、图片转换都是在下面步骤执行

7、具体看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);
  }
复制代码

这里首先通过构建EngineKey,判断内存缓存中是否命中。接着判断jobs队列中是否已存在该任务。否则会构建EngineJob、DecodeJob,并通过engineJob.start(decodeJob),通过线程池去执行DecodeJob任务。DecodeJob实现了Runnable接口,所以我们接着分析DecodeJob的run方法

8、DecodeJob的run方法最后执行了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);
    }
  }
复制代码

根据不同的runReason执行不同的任务,共两种任务类型:

runGenerators():load数据

decodeFromRetrievedData():处理已经load到的数据

RunReason再次执行任务的原因,三种枚举值: INITIALIZE:第一次调度任务

WITCH_TO_SOURCE_SERVICE:本地缓存策略失败,尝试重新获取数据,两种情况;当stage为Stage.SOURCE,或者获取数据失败并且执行和回调发生在了不同的线程

DECODE_DATA:获取数据成功,但执行和回调不在同一线程,希望回到自己的线程去处理数据。

9、getNextStage()是获取加载资源的策略,一共5种策略: INITIALIZE,RESOURCE_CACHE,DATA_CACHE,SOURCE,FINISHED

其中加载数据的策略有三种: RESOURCE_CACHE,DATA_CACHE,SOURCE, 分别对应的Generator:

ResourceCacheGenerator :尝试从修改过的资源缓存中获取,如果缓存未命中,尝试从DATA_CACHE中获取

DataCacheGenerator :尝试从未修改过的本地缓存中获取数据,如果缓存未命中则尝试从SourceGenerator中获取

SourceGenerator :从原始的资源中获取,可能是服务器,也可能是本地的一些原始资源 策略的配置在DiskCacheStrategy。开发者可通过BaseRequestOptions设置: ALL NONE DATA RESOURCE AUTOMATIC(默认方式,依赖于DataFetcher的数据源和ResourceEncoder的EncodeStrategy)

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);
    }
  }
复制代码

10、getNextGenerator,根据Stage获取到相应的Generator后会执行currentGenerator.startNext(),如果中途startNext返回true,则直接回调,否则最终会得到SOURCE的stage,重新调度任务。

  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;
      }
    }
    // We've run out of stages and generators, give up.
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }

    // Otherwise a generator started a new load and we expect to be called back in
    // onDataFetcherReady.
  }
复制代码

11、这里我们分析最后SourceGenerator的startNext的执行,实现如下:

  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;
  }
复制代码

最后会通过Glide初始化时,Register注册的ModelLoader去执行对应的loadData方法,最后回调onDataFetcherReady(),获取得到DataSource,并将 runReason = RunReason.DECODE_DATA。触发调用decodeFromRetrievedData()进行源数据的转换

12、decodeFromRetrievedData,获取数据成功后,进行处理,内部调用的是runLoadPath(Data data, DataSource dataSource,LoadPath<Data, ResourceType, R> path),decode完成后的回调,对decode的资源进行transform。

 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();
    }
  }
  
  
    @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);
  }
复制代码

13、decodeFromRetrievedData()中,数据decode和transform后会执行notifyEncodeAndRelease方法,在该方法中调用 notifyComplete(result, dataSource),接着调用callback.onResourceReady,实现如下:

  @Override
  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    this.resource = resource;
    this.dataSource = dataSource;
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
  }

复制代码

14、通过Handler将回调切换到主线程, 最后调用EngineJob的handleResultOnMainThread方法,实现如下:

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;

    // Hold on to resource for duration of request so we don't recycle it in the middle of
    // notifying if it synchronously released by one of the callbacks.
    engineResource.acquire();
    listener.onEngineJobComplete(this, key, engineResource);

    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = cbs.size(); i < size; i++) {
      ResourceCallback cb = cbs.get(i);
      if (!isInIgnoredCallbacks(cb)) {
        engineResource.acquire();
        cb.onResourceReady(engineResource, dataSource);
      }
    }
    // Our request is complete, so we can release the resource.
    engineResource.release();

    release(false /*isRemovedFromQueue*/);
  }
复制代码

15、进行相关的资源清理后,最后调用SingleRequest的onResourceReady(Resource<?> resource, DataSource dataSource) 方法,最后调用target.onResourceReady(result, animation)方法,实现资源的显示,代码如下:

  private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;

    if (glideContext.getLogLevel() <= Log.DEBUG) {
      Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "
          + dataSource + " for " + model + " with size [" + width + "x" + height + "] in "
          + LogTime.getElapsedMillis(startTime) + " ms");
    }

    isCallingCallbacks = true;
    try {
      if ((requestListener == null
          || !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource))
          && (targetListener == null
          || !targetListener.onResourceReady(result, model, target, dataSource, isFirstResource))) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }

    notifyLoadSuccess();
  }
复制代码

Glide是如何与Activity及Fragment等的生命周期绑定?

Glide在执行with的阶段,会根据context的类型,将Glide的Request请求与context类型进行绑定。Application类型为整个应用的生命周期。Fragment及Activity类型,通过巧妙的设计一个RequestManagerFragment,加入到Activity或Fragment当中,从而实现生命周期的监听。

1、Application的绑定为ApplicationLifecycle,与App的生命周期一致

  @Override
 public void addListener(@NonNull LifecycleListener listener) {
   listener.onStart();
 }

 @Override
 public void removeListener(@NonNull LifecycleListener listener) {
   // Do nothing.
 }

复制代码

2、Activity或Fragment的绑定为ActivityFragmentLifecycle,与宿主对应的生命周期一致。

public interface LifecycleListener {

  /**
   * Callback for when {@link android.app.Fragment#onStart()}} or {@link
   * android.app.Activity#onStart()} is called.
   */
  void onStart();

  /**
   * Callback for when {@link android.app.Fragment#onStop()}} or {@link
   * android.app.Activity#onStop()}} is called.
   */
  void onStop();

  /**
   * Callback for when {@link android.app.Fragment#onDestroy()}} or {@link
   * android.app.Activity#onDestroy()} is called.
   */
  void onDestroy();
}
复制代码

3、在RequestManger中实现了监听接口的注册,代码如下:

  private final Runnable addSelfToLifecycle = new Runnable() {
    @Override
    public void run() {
      lifecycle.addListener(RequestManager.this);
    }
  };
复制代码

这里注意,一个页面拥有一个RequestManagerFragment,RequestManagerFragment会持有RequestManger的引用。一个页面发起多个Glide显示图片请求,会优先从Fragment中获取RequestManger,不会重复创建多个RequestManger对象。

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) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
复制代码

4、RequestManger中绑定的回调执行

  /**
   * Lifecycle callback that registers for connectivity events (if the
   * android.permission.ACCESS_NETWORK_STATE permission is present) and restarts failed or paused
   * requests.
   */
  @Override
  public 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 void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }

  /**
   * Lifecycle callback that cancels all in progress requests and clears and recycles resources for
   * all completed requests.
   */
  @Override
  public 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);
  }
复制代码

从实现可知,当Activity或Fragment退到后台时,会调用pauseRequests()暂停请求,回到前台时会重新执行请求,当页面销毁时,会进行对应的资源清理及回收。

image

Glide的缓存实现原理是怎样的?

简介

image

Glide的缓存使用内存缓存及硬盘缓存进行处理。

缓存 说明
ActiveResources ActiveResources是一个以弱引用资源为value。用于缓存正在使用的资源
MemoryCache MemoryCache是使用LruResourceCache实现,用于缓存非正在使用的资源
DiskCache 进行资源磁盘缓存
Http 通过网络地址,从服务端加载资源文件

假如用户配置了使用内存缓存及磁盘缓存,则主要的加载实现流程如下:

1、当发起Request时,首先会从ActiveResources中进行缓存查找。如果命中则返回显示,如果不命中,则从MemoryCache中获取。当资源从ActiveResources中移除后,加入到MemoryCache中

2、当在MemoryCache中命中时,则会将资源加入到ActiveResources中,并在该Cache中移除,如果不命中则会尝试从磁盘缓存中进行加载

3、根据用于配置的策略,如果在磁盘缓存中命中,则会返回,并将资源缓存到ActiveResources当中,如果不命中,则会将进行网络的请求

4、根据ModelLoader的配置实现,从网络中加载资源,并根据配置,缓存到磁盘及内存缓存中

Key

根据流程分析,我们知道Key的生成在Engine的load方法中,具体的实现如下:

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

可见为了兼容复杂的资源转换,保证key的唯一性,包含了非常多的参数进行构建。主要有model(目标地址)、signature(设置的签名)、图片的width、heigh、资源的转换配置等。

内存缓存

根据上面的简介,我们可以知道,Glide主要的内存缓存策略采用了2级缓存,为ActiveResources和MemoryCache。下面我们从源码的角度分析这2个缓存的机制。

ActiveResources

1、 根据源码,我们可知内存采用了一个HashMap进行内存的缓存,使用了弱引用ResourceWeakReference持有了Resource

 final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
复制代码

2、当获取资源时,主要采用get方法进行获取,实现如下:

EngineResource<?> get(Key key) {
    ResourceWeakReference activeRef = activeEngineResources.get(key);
    if (activeRef == null) {
      return null;
    }

    EngineResource<?> active = activeRef.get();
    if (active == null) {
      cleanupActiveReference(activeRef);
    }
    return active;
  }
复制代码

如果命中,就返回资源。这里注意,如果当active==null,引用被回收时,会调用cleanupActiveReference方法,实现如下:

void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
    Util.assertMainThread();
    activeEngineResources.remove(ref.key);

    if (!ref.isCacheable || ref.resource == null) {
      return;
    }
    EngineResource<?> newResource =
        new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
    newResource.setResourceListener(ref.key, listener);
    listener.onResourceReleased(ref.key, newResource);
  }

复制代码

如果ref.resource!=null,则会重新生成一个EngineResource对象,并回调onResourceReleased方法,我们看具体的实现如下:

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

从源码可知,会调用deactivate方法,从activeResources中移除,然后加入到MemoryCache中。

3、写入资源

 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();
    }
  }
复制代码

EngineResource

EngineResource中主要为了一个acquired变量

  private int acquired;
复制代码

当资源被使用时,会调用acquire,将变量值+1

  void acquire() {
    if (isRecycled) {
      throw new IllegalStateException("Cannot acquire a recycled resource");
    }
    if (!Looper.getMainLooper().equals(Looper.myLooper())) {
      throw new IllegalThreadStateException("Must call acquire on the main thread");
    }
    ++acquired;
  }
复制代码

当资源被释放时,会调用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);
    }
  }
复制代码

这里注意,当acquired == 0,表明资源没有被使用时,则会调用onResourceReleased,将资源存储到MemoryCache中。这样也就实现了正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。

MemoryCache

Glide在Build的过程中会创建具体的MemoryCache对象,具体的实现如下:

 if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }
复制代码

从源码可知,MemoryCache的主要实现是采用了LRU算法,我们具体查看LruResourceCache的实现。发现其继承与LruCache,LruCache的关键实现如下:

  private final Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);

复制代码

从源码可知,Glide的内存缓存的LRU算法实现主要是使用了LinkedHashMap。

这里详细的说明可参考: 如何用LinkedHashMap实现LRU缓存算法

BitmapPool

Glide内部维护了一个BitmapPool池,用于Bitmap的复用,可优化GC的回收。 在GlideBuilder中进行了实例,实现如下:

 if (bitmapPool == null) {
      int size = memorySizeCalculator.getBitmapPoolSize();
      if (size > 0) {
        bitmapPool = new LruBitmapPool(size);
      } else {
        bitmapPool = new BitmapPoolAdapter();
      }
    }
复制代码

我们具体看LruBitmapPool的实现代码

1、 put

 @Override
  public synchronized void put(Bitmap bitmap) {
    if (bitmap == null) {
      throw new NullPointerException("Bitmap must not be null");
    }
    if (bitmap.isRecycled()) {
      throw new IllegalStateException("Cannot pool recycled bitmap");
    }
    if (!bitmap.isMutable() || strategy.getSize(bitmap) > maxSize
        || !allowedConfigs.contains(bitmap.getConfig())) {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Reject bitmap from pool"
                + ", bitmap: " + strategy.logBitmap(bitmap)
                + ", is mutable: " + bitmap.isMutable()
                + ", is allowed config: " + allowedConfigs.contains(bitmap.getConfig()));
      }
      bitmap.recycle();
      return;
    }

    final int size = strategy.getSize(bitmap);
    strategy.put(bitmap);
    tracker.add(bitmap);

    puts++;
    currentSize += size;

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Put bitmap in pool=" + strategy.logBitmap(bitmap));
    }
    dump();

    evict();
  }

复制代码

做了一系列的非空、回收判断后,最后将bitmap加入到strategy中,加入到缓存中

2、get

  @Override
  @NonNull
  public Bitmap get(int width, int height, Bitmap.Config config) {
    Bitmap result = getDirtyOrNull(width, height, config);
    if (result != null) {
      // Bitmaps in the pool contain random data that in some cases must be cleared for an image
      // to be rendered correctly. we shouldn't force all consumers to independently erase the
      // contents individually, so we do so here. See issue #131.
      result.eraseColor(Color.TRANSPARENT);
    } else {
      result = createBitmap(width, height, config);
    }

    return result;
  }
复制代码

当需要生成Bitmap中,根据宽高及config从池中获取,如果没有,则调用createBtimao创建一个。如果命中,则会提取出Bitmap,然后将像素都设置为透明,同时从池中移除,并返回。

总结一下

glide采用了2级的内存缓存,activeResources是一个以弱引用资源为value,的map,memory是使用LruResourceCache实现的。就是activeResources是一个随时有可能被回收资源。它存在的意义在于,memory的强引用的频繁读写也有可能造成内存激增频繁GC,而造成内存抖动。资源在使用的过程中将会保存在activeResources中,而activeResources是弱引用的,可以随时被系统回收,不会造成内存泄漏和过多的使用

硬盘缓存

缓存策略

Glide缓存的资源分为两种(1,原图(SOURCE)原始图片 2,处理图(RESULT)经过压缩和变形等转化的图片)

硬盘缓存分为五种,具体看一面。可以通过调用diskCacheStrategy()方法并传入五种不同的参数

1,DiskCacheStrategy.NONE// 表示不缓存任何内容

2,DiskCacheStrategy.DATA// 表示只缓存原始图片

3,DiskCacheStrategy.RESOURCE// 表示只缓存转换过后的图片

4,DiskCacheStrategy.ALL // 表示既缓存原始图片,也缓存转换过后的图片

5,DiskCacheStrategy.AUTOMATIC//表示让Glide根据图片资源智能地选择使用哪一种缓存策略(默认选项)

缓存的获取

1、根据上述的流程分析,我们知道具体磁盘缓存在DecodeJob中执行,当任务开始时,调用了runWrapped()方法,接着会调用getNextStage,这里会获取磁盘的加载策略Stage,具体实现如下:

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

复制代码

接着会调用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);
    }
  }
复制代码

这里获取下一步执行的策略,一共5种策略: INITIALIZE,RESOURCE_CACHE,DATA_CACHE,SOURCE,FINISHED

其中加载数据的策略有三种: RESOURCE_CACHE,DATA_CACHE,SOURCE, 分别对应的Generator:

ResourceCacheGenerator :尝试从修改过的资源缓存中获取,如果缓存未命中,尝试从DATA_CACHE中获取

DataCacheGenerator :尝试从未修改过的本地缓存中获取数据,如果缓存未命中则尝试从SourceGenerator中获取

SourceGenerator :从原始的资源中获取,可能是服务器,也可能是本地的一些原始资源

接着调用具体的Generator的startNext方法。磁盘的缓存获取实现在这个方法中获取。 这里ResourceCacheGenerator的关键缓存获取代码如下:

 Key sourceId = sourceIds.get(sourceIdIndex);
      Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
      Transformation<?> transformation = helper.getTransformation(resourceClass);
      // PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
      // we only run until the first one succeeds, the loop runs for only a limited
      // number of iterations on the order of 10-20 in the worst case.
      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;
      }
复制代码

DataCacheGenerator的关键缓存获取代码如下:

  Key sourceId = cacheKeys.get(sourceIdIndex);
      // PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
      // and the actions it performs are much more expensive than a single allocation.
      @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
      Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
      cacheFile = helper.getDiskCache().get(originalKey);
      if (cacheFile != null) {
        this.sourceKey = sourceId;
        modelLoaders = helper.getModelLoaders(cacheFile);
        modelLoaderIndex = 0;
      }
    }
复制代码

缓存的写入

1、Data数据的缓存

从服务端获取数据的主要实现在SourceGenerator中,我们查看源码onDataReady可知其中判断了isDataCacheable()会将数据赋值到dataToCache中。重新触发reschedule();

  @Override
  public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      dataToCache = data;
      // We might be being called back on someone else's thread. Before doing anything, we should
      // reschedule to get back onto Glide's thread.
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
  }
复制代码

当再次出发startNext,关键实现如下:

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

其中的cacheData,将原始的Data数据缓存到磁盘文件中,代码实现如下:

 private void cacheData(Object dataToCache) {
    long startTime = LogTime.getLogTime();
    try {
      Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
      DataCacheWriter<Object> writer =
          new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
      originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
      helper.getDiskCache().put(originalKey, writer);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished encoding source to cache"
            + ", key: " + originalKey
            + ", data: " + dataToCache
            + ", encoder: " + encoder
            + ", duration: " + LogTime.getElapsedMillis(startTime));
      }
    } finally {
      loadData.fetcher.cleanup();
    }

    sourceCacheGenerator =
        new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }
复制代码

2、Resource数据的缓存

根据上述的流程分析,再DecodeJob中的onResourceDecoded回调中,关键的实现如下:

 if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
        encodeStrategy)) {
      if (encoder == null) {
        throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
      }
      final Key key;
      switch (encodeStrategy) {
        case SOURCE:
          key = new DataCacheKey(currentSourceKey, signature);
          break;
        case TRANSFORMED:
          key =
              new ResourceCacheKey(
                  decodeHelper.getArrayPool(),
                  currentSourceKey,
                  signature,
                  width,
                  height,
                  appliedTransformation,
                  resourceSubClass,
                  options);
          break;
        default:
          throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
      }

      LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
      deferredEncodeManager.init(key, encoder, lockedResult);
      result = lockedResult;
    }
复制代码

会初始化DeferredEncodeManager对象。接着会执行到notifyEncodeAndRelease()方法,其中关键的实现如下:

 try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }
复制代码

在encode中进行了resource数据的缓存,代码如下:

   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();
      }
    }
复制代码

缓存的实现

1、在Glide的Build方法中,我们可以看到磁盘缓存的工厂实例,代码如下:

if (diskCacheFactory == null) {
      diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }
复制代码

2、InternalCacheDiskCacheFactory继承了DiskLruCacheFactory,工厂关键的build方法如下:

 @Override
  public DiskCache build() {
    File cacheDir = cacheDirectoryGetter.getCacheDirectory();

    if (cacheDir == null) {
      return null;
    }

    if (!cacheDir.mkdirs() && (!cacheDir.exists() || !cacheDir.isDirectory())) {
      return null;
    }

    return DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
  }
复制代码

3、可见实际的磁盘缓存对象为DiskLruCacheWrapper类,我们看对应的get、put方法,实现如下:

 @Override
  public File get(Key key) {
    String safeKey = safeKeyGenerator.getSafeKey(key);
    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      Log.v(TAG, "Get: Obtained: " + safeKey + " for for Key: " + key);
    }
    File result = null;
    try {
      // It is possible that the there will be a put in between these two gets. If so that shouldn't
      // be a problem because we will always put the same value at the same key so our input streams
      // will still represent the same data.
      final DiskLruCache.Value value = getDiskCache().get(safeKey);
      if (value != null) {
        result = value.getFile(0);
      }
    } catch (IOException e) {
      if (Log.isLoggable(TAG, Log.WARN)) {
        Log.w(TAG, "Unable to get from disk cache", e);
      }
    }
    return result;
  }

  @Override
  public void put(Key key, Writer writer) {
    // We want to make sure that puts block so that data is available when put completes. We may
    // actually not write any data if we find that data is written by the time we acquire the lock.
    String safeKey = safeKeyGenerator.getSafeKey(key);
    writeLocker.acquire(safeKey);
    try {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Put: Obtained: " + safeKey + " for for Key: " + key);
      }
      try {
        // We assume we only need to put once, so if data was written while we were trying to get
        // the lock, we can simply abort.
        DiskLruCache diskCache = getDiskCache();
        Value current = diskCache.get(safeKey);
        if (current != null) {
          return;
        }

        DiskLruCache.Editor editor = diskCache.edit(safeKey);
        if (editor == null) {
          throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
        }
        try {
          File file = editor.getFile(0);
          if (writer.write(file)) {
            editor.commit();
          }
        } finally {
          editor.abortUnlessCommitted();
        }
      } catch (IOException e) {
        if (Log.isLoggable(TAG, Log.WARN)) {
          Log.w(TAG, "Unable to put to disk cache", e);
        }
      }
    } finally {
      writeLocker.release(safeKey);
    }
  }
复制代码

4、最终可知磁盘缓存的实现为DiskLruCache,关于DiskLruCache缓存可参考如下博文

DiskLruCache缓存

Glide的底层网络实现是什么?

通过上述的流程分析,我们可知最后的网络加载在SourceGenerator中的startNext()方法,通过初始注册的ModelLoader对应的DataFetcher去加载数据。我们以load一个GlideUrl地址来分析

在Glide的构造函数中,Register会注册ModelLoader,代码实现如下:

 .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
复制代码

在SourceGenerator中的startNext()方法中会循环匹配出对应的ModelLoader,实现如下:

  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);
      }
    }
复制代码

我们查看HttpGlideUrlLoader,其最后的LoadData实现为HttpUrlFetcher,具体代码如下:

 @Override
  public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
      @NonNull Options options) {
    // GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
    // spent parsing urls.
    GlideUrl url = model;
    if (modelCache != null) {
      url = modelCache.get(model, 0, 0);
      if (url == null) {
        modelCache.put(model, 0, 0, model);
        url = model;
      }
    }
    int timeout = options.get(TIMEOUT);
    return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
  }
复制代码

HttpUrlFetcher具体加载网络的数据的实现如下:

@Override
  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> 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));
      }
    }
  }
  
   private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
      Map<String, String> headers) throws IOException {
    if (redirects >= MAXIMUM_REDIRECTS) {
      throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
    } else {
      // Comparing the URLs using .equals performs additional network I/O and is generally broken.
      // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
      try {
        if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
          throw new HttpException("In re-direct loop");

        }
      } catch (URISyntaxException e) {
        // Do nothing, this is best effort.
      }
    }

    urlConnection = connectionFactory.build(url);
    for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
      urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
    }
    urlConnection.setConnectTimeout(timeout);
    urlConnection.setReadTimeout(timeout);
    urlConnection.setUseCaches(false);
    urlConnection.setDoInput(true);

    // Stop the urlConnection instance of HttpUrlConnection from following redirects so that
    // redirects will be handled by recursive calls to this method, loadDataWithRedirects.
    urlConnection.setInstanceFollowRedirects(false);

    // Connect explicitly to avoid errors in decoders if connection fails.
    urlConnection.connect();
    // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
    stream = urlConnection.getInputStream();
    if (isCancelled) {
      return null;
    }
    final int statusCode = urlConnection.getResponseCode();
    if (isHttpOk(statusCode)) {
      return getStreamForSuccessfulRequest(urlConnection);
    } else if (isHttpRedirect(statusCode)) {
      String redirectUrlString = urlConnection.getHeaderField("Location");
      if (TextUtils.isEmpty(redirectUrlString)) {
        throw new HttpException("Received empty or null redirect url");
      }
      URL redirectUrl = new URL(url, redirectUrlString);
      // Closing the stream specifically is required to avoid leaking ResponseBodys in addition
      // to disconnecting the url connection below. See #2352.
      cleanup();
      return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
    } else if (statusCode == INVALID_STATUS_CODE) {
      throw new HttpException(statusCode);
    } else {
      throw new HttpException(urlConnection.getResponseMessage(), statusCode);
    }
  }
复制代码

通过分析可知,Glide默认的网络加载使用的是urlConnection。当然我们也可以通过自定义ModelLoader,使用okhttp、volley等的网络框架进行加载。

具体可参考博文 Glide 4.x添加自定义组件原理

Glide中代码运用了那些设计模式,有什么巧妙的设计?

1、建造者模式

Glide对象的创建使用Build模式,将复杂对象的创建和表示分离,调用者不需要知道复杂的创建过程,使用Build的相关方法进行配置创建对象。

2、外观模式

Glide对外提供了统一的调度,屏蔽了内部的实现,使得使用该网络库简单便捷。

3、策略模式

关于DecodeJob中的DataFetcherGenerator资源获取,采用了策略模式,将数据加载的不同算法进行封装。

4、工厂模式

ModelLoader的创建使用了ModelLoaderFactory、Engine中的EngineJobFactory、DiskLruCacheFactory等

总结

思考

Glide正因为其强大的功能,高效的运行机制,所以源码的实现非常复杂。在学习的过程中也遇到很多的困难,最终还是一步一步坚持下来了。有时候放弃就是一瞬间的念头,但坚持下来,终究会有收获。

参考资料

Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

如何用LinkedHashMap实现LRU缓存算法

DiskLruCache缓存

Glide解析-cache

Glide 4.x添加自定义组件原理

Android Glide源码分析

推荐

Android源码系列-解密OkHttp

Android源码系列-解密Retrofit

Android源码系列-解密Glide

Android源码系列-解密EventBus

Android源码系列-解密RxJava

Android源码系列-解密LeakCanary

Android源码系列-解密BlockCanary

关于

欢迎关注我的个人公众号

微信搜索:一码一浮生,或者搜索公众号ID:life2code

image