Glide源码学习随笔

作者: 黄俊彬 | 来源:发表于2018-08-13 17:12 被阅读45次

    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 格式

    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缓存算法

    总结一下

    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源码分析

    相关文章

      网友评论

      • 9e3f9aee57c7:一直在关注着老师的笔录。支持一下

      本文标题:Glide源码学习随笔

      本文链接:https://www.haomeiwen.com/subject/rsmubftx.html