美文网首页
Android Glide 源码分析和学习

Android Glide 源码分析和学习

作者: 过期的薯条 | 来源:发表于2020-11-15 14:04 被阅读0次

    1.引言

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

    2.正题

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

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

    2.1 Glide总体加载流程

    未命名文件.jpg

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

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

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

    2.1.1 Request产生阶段

    Glide类:

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

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

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

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

    RequestManagerRetriever类:

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

    GlideBuilder类:

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

    RequestManager类:

    RequestManager以Manager,肯定是包含了:

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

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

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

    private <Y extends Target<TranscodeType>> Y into(
          @NonNull Y target,
          @Nullable RequestListener<TranscodeType> targetListener,
          BaseRequestOptions<?> options,
          Executor callbackExecutor) {
        Preconditions.checkNotNull(target);
        if (!isModelSet) {
          throw new IllegalArgumentException("You must call #load() before calling #into()");
        }
    
        Request request = buildRequest(target, targetListener, options, callbackExecutor);//
    
        Request previous = target.getRequest();//判断此请求是否被加载过
        if (request.isEquivalentTo(previous)
            && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
          // If the request is completed, beginning again will ensure the result is re-delivered,
          // triggering RequestListeners and Targets. If the request is failed, beginning again will
          // restart the request, giving it another chance to complete. If the request is already
          // running, we can let it continue running without interruption.
          if (!Preconditions.checkNotNull(previous).isRunning()) {
            // Use the previous request rather than the new one to allow for optimizations like skipping
            // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
            // that are done in the individual Request.
            previous.begin();//假如Request存在的话 就直接走begin开始走Engin流程
          }
          return target;
        }
    
        requestManager.clear(target);
        target.setRequest(request);
        requestManager.track(target, request);//开启网络请求
    
        return target;
      }
    

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

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

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

    解答1:

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

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

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

    image.png

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

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

    image.png

    2.1.2 加载Request

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

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

    RunRequest方法 代码如下:

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

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

    image.png

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

    SingleRequest#begin方法如下:

    @Override
      public void begin() {
        synchronized (requestLock) {
         
          //加载完成从内存中去取
          if (status == Status.COMPLETE) {
            onResourceReady(
                resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);        return;
          }
    
          // Restarts for requests that are neither complete nor running can be treated as new requests
          // and can run again from the beginning.
    
          status = Status.WAITING_FOR_SIZE;
          if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
            onSizeReady(overrideWidth, overrideHeight);
          } else {
            target.getSize(this);
          }
    
          if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
              && canNotifyStatusChanged()) {
            target.onLoadStarted(getPlaceholderDrawable());
          }
          if (IS_VERBOSE_LOGGABLE) {
            logV("finished run method in " + LogTime.getElapsedMillis(startTime));
          }
        }
      }
    

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

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

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

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

    Engine#load:

    public <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,
          Executor callbackExecutor) {
        long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
    
        //标记EngineResource的独一无二的key
        EngineKey key =
            keyFactory.buildKey(
                model,
                signature,
                width,
                height,
                transformations,
                resourceClass,
                transcodeClass,
                options);
    
        EngineResource<?> memoryResource;
        synchronized (this) {
          memoryResource = loadFromMemory(key, isMemoryCacheable, startTime); //缓存机制1:从内存加载
          if (memoryResource == null) {
            return waitForExistingOrStartNewJob(
                glideContext,
                model,
                signature,
                width,
                height,
                resourceClass,
                transcodeClass,
                priority,
                diskCacheStrategy,
                transformations,
                isTransformationRequired,
                isScaleOnlyOrNoTransform,
                options,
                isMemoryCacheable,
                useUnlimitedSourceExecutorPool,
                useAnimationPool,
                onlyRetrieveFromCache,
                cb,
                callbackExecutor,
                key,
                startTime);
          }
        }
    

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

    @Nullable
      private EngineResource<?> loadFromMemory(
          EngineKey key, boolean isMemoryCacheable, long startTime) {
        if (!isMemoryCacheable) {
          return null;
        }
    
        EngineResource<?> active = loadFromActiveResources(key);
        if (active != null) {
          if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from active resources", startTime, key);
          }
          return active;
        }
    
        EngineResource<?> cached = loadFromCache(key);
        if (cached != null) {
          if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
          }
          return cached;
        }
    
        return null;
      }
    

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

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

    ActiveResources类:

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

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

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

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

      private EngineResource<?> loadFromCache(Key key) {
        EngineResource<?> cached = getEngineResourceFromCache(key);
        if (cached != null) {
          cached.acquire();
          activeResources.activate(key, cached);
        }
        return cached;
      }
    
    private EngineResource<?> getEngineResourceFromCache(Key key) {
        Resource<?> cached = cache.remove(key);
    
        final EngineResource<?> result;
        if (cached == null) {
          result = null;
        } else if (cached instanceof EngineResource) {
          // Save an object allocation if we've cached an EngineResource (the typical case).
          result = (EngineResource<?>) cached;
        } else {
          result =
              new EngineResource<>(
                  cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
        }
        return result;
      }
    
    

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

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

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

    流程图:


    image.png

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

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

    EngineJob类:

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

    image.png

    DecodeJob类:

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

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

    @SuppressWarnings("PMD.AvoidRethrowingException")
      @Override
      public void run() {
        GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
        DataFetcher<?> localFetcher = currentFetcher;
        try {
          if (isCancelled) {
            notifyFailed();
            return;
          }
          runWrapped();//关键
        } catch (CallbackException e) {
          throw e;
        } catch (Throwable t) {
      }
    
      private void runWrapped() {
        switch (runReason) {
          case INITIALIZE:
            stage = getNextStage(Stage.INITIALIZE);
            currentGenerator = getNextGenerator();
            runGenerators();
            break;
          case SWITCH_TO_SOURCE_SERVICE:
            runGenerators();
            break;
          case DECODE_DATA:
            decodeFromRetrievedData();
            break;
          default:
            throw new IllegalStateException("Unrecognized run reason: " + runReason);
        }
      }
    
      private DataFetcherGenerator getNextGenerator() {
        switch (stage) {
          case RESOURCE_CACHE:
            return new ResourceCacheGenerator(decodeHelper, this);
          case DATA_CACHE:
            return new DataCacheGenerator(decodeHelper, this);
          case SOURCE:
            return new SourceGenerator(decodeHelper, this);
          case FINISHED:
            return null;
          default:
            throw new IllegalStateException("Unrecognized stage: " + stage);
        }
      }
    

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

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

    runWrapped() 调用顺序

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

    ResourceCacheGenerator.startNext方法如下:

     currentKey =
              new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
                  helper.getArrayPool(),
                  sourceId,
                  helper.getSignature(),
                  helper.getWidth(),
                  helper.getHeight(),
                  transformation,
                  resourceClass,
                  helper.getOptions());
          cacheFile = helper.getDiskCache().get(currentKey);
          if (cacheFile != null) {
            sourceKey = sourceId;
            modelLoaders = helper.getModelLoaders(cacheFile);
            modelLoaderIndex = 0;
          }
        }
    
        loadData = null;
        boolean started = false;
        while (!started && hasNextModelLoader()) {
          ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
          loadData =
              modelLoader.buildLoadData(
                  cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
          if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
            started = true;
            loadData.fetcher.startNext(helper.getPriority(), this);
          }
        }
    

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

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

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

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

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

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

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

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

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

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

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

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

    完整的内存缓存图:

    image.png

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

    image.png

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

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

    接下来看看 HttpUrlFetcher#loadData

    @Override
      public void loadData(
          @NonNull Priority priority, @NonNull DataCallback<? 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));
          }
        }
      }
    

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

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

    2.1.3 请求回调处理

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

    image.png

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

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

    相关文章

      网友评论

          本文标题:Android Glide 源码分析和学习

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