【Glide】- 源码分析

简介

Glide由Google推出的图片加载框架,支持多种图片格式,同类的还有picasso,fresco。picasso功能没有Glide强大,也不支持gif加载,fresco是Facebook公司推出的框架。

添加glide

// glide
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'

实例

Glide.with(targetImageView.getContext())
      .load(headerUrl)
      .into(targetImageView);

Glide初始化

在调用with方法的时候,如果Glide没有初始化,那么会先进行初始化,创建Glide实例。

Glide编译

在编译过程中,Glide注解程序会生成GeneratedAppGlideModuleImpl和GeneratedRequestManagerFactory两个类来帮助Glide的初始化。

创建GeneratedAppGlideModuleImpl对象

通过创建GeneratedAppGlideModuleImpl对象来获取初始化和配置信息实例,并组装到glide实例中。

创建Glide初始化配置实例

自定义ConfigGlideModule,并继承于AppGlideModule抽象类,通过“GlideModule”注解或者在AndroidManifest配置来指定。

  1. 注解方式
    @com.bumptech.glide.annotation.GlideModule
     public class ConfigGlideModule extends AppGlideModule {
         @Override
         public boolean isManifestParsingEnabled() {return false;}
    
         @Override
         public void applyOptions(Context context, GlideBuilder builder) {super.applyOptions(context, builder);}
     }
    
  2. 清单文件配置
    
        
    
    
with过程

创建初始化Glide实例,调用with方法对glide进行初始化,with()方法可以接收Context、Activity,Fragment类型参数。如果调用的地方不在Activity,Fragment中,可以获取当前应用程序的ApplicationContext,传入到with()方法中。如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止。

Glide类中存在下面两个变量。

  private static volatile Glide glide;
  private static volatile boolean isInitializing;

如果glide是null,才会去创建glide对象,如果isInitializing是true,代表glide正在初始化,这时候如果再次初始化,是直接会抛异常的,所以在多线程使用环境中一定要注意。

  • 全局初始化配置
    1. 调用自定义的初始化配置类的applyOptions和registerComponents方法
       // 通过manifest配置
      for (com.bumptech.glide.module.GlideModule module : manifestModules) {
         module.applyOptions(applicationContext, builder);
         module.registerComponents(applicationContext, glide, glide.registry);
       }
       // 通过“GlideModule”注解配置
       if (annotationGeneratedModule != null) {
         annotationGeneratedModule.applyOptions(applicationContext, builder);
         annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
       }
      
  • GlideBuilder(组装Glide实例)
    1. 调用GeneratedAppGlideModuleImpl的getRequestManagerFactory获取GeneratedRequestManagerFactory实例并设置到glide中。
     builder.setRequestManagerFactory(factory);
    
  • 注册ComponentCallbacks
    applicationContext.registerComponentCallbacks(glide);
    

load过程

  • 创建RequestBuilder
    public  RequestBuilder as(
       @NonNull Class resourceClass) {
     return new RequestBuilder<>(glide, this, resourceClass, context);
     }
    
    resourceClass默认是Drawable.class,可以通过调用 asGif,.asBitmap等方法来改变
    Glide.with(targetImageView.getContext())
           .asBitmap()
           .load(internetUrl)                
           .apply(getRoundedCornersOptions(dip2px(targetImageView.getContext(), cornerRadios)))
           .into(targetImageView);
    
    保存load传入的参数
    model的值就是load传入的参数
    private RequestBuilder loadGeneric(@Nullable Object model) {
      this.model = model;
      isModelSet = true;
      return this;
    }
    

into过程

glide需要工作在主线程,否则直接抛异常。

  • 请求配置(BaseRequestOptions)

        if (!requestOptions.isTransformationSet()
         && requestOptions.isTransformationAllowed()
         && view.getScaleType() != null) {
       switch (view.getScaleType()) {
         case CENTER_CROP:
           requestOptions = requestOptions.clone().optionalCenterCrop();
           break;
         case CENTER_INSIDE:
           requestOptions = requestOptions.clone().optionalCenterInside();
           break;
         case FIT_CENTER:
         case FIT_START:
         case FIT_END:
           requestOptions = requestOptions.clone().optionalFitCenter();
           break;
         case FIT_XY:
           requestOptions = requestOptions.clone().optionalCenterInside();
           break;
         case CENTER:
         case MATRIX:
         default:
       }
     }
    
    • 构建请求
      Request request = buildRequest(target, targetListener, options, callbackExecutor);
      
      1. 构建主请求实例mainRequest
        里面重点就是创建请求实例,创建请求实例分两种情况。
        • 需求请求缩略图
          如果有缩略图将创建全图fullRequest(SingleRequest类型)和缩略图thumbnailRequest(SingleRequest类型)请求实例,并且会创建一个持有这两个请求实例的协同请求实例coordinator(ThumbnailRequestCoordinator类型),
        • 不需要缩略图
          直接创建全图fullRequest(SingleRequest类型)请求实例。
      2. 创建错误请求实例
        如果错误请求协同实例errorRequestCoordinator是为null,那么会创建主请求实例和错误请求实例,并且设置到errorRequestCoordinator(ErrorRequestCoordinator类型)中。
  • 获取缓存Request

    Request previous = target.getRequest();
    

    如果首次加载图片,previous是null,在正式加载前,调用目标View的setTag方法将请求实例request保存到View中,如果获得到的实例不是Request类型,那么将直接抛异常

     "You must not call setTag() on a view Glide is targeting"
    

    所以用glide是不能在给View设置tag的,否则会报异常。

    判断是否是同一个任务逻辑如下:

    return localOverrideWidth == otherLocalOverrideWidth
          && localOverrideHeight == otherLocalOverrideHeight
          // 需要加载的图片路径。比如url
          && Util.bothModelsNullEquivalentOrEquals(localModel, otherLocalModel)
          // 转换类型
          && localTransocdeClass.equals(otherLocalTransocdeClass)
          && localRequestOptions.equals(otherLocalRequestOptions)
          && localPriority == otherLocalPriority  
          && localListenerCount == otherLocalListenerCount;
    

    大概意思就是,请求的图片路径,应用到目标View的基本参数,任务优先级等相同就任务是相同任务请求。

    如果是同一任务,有下面几种情况

    1. 开启缓存
      • 如果请求已完成,则重新开始重新传递请求结果,从而触发RequestListeners和Targets
      • 如果请求失败,则重新启动请求,完成请求。
      • 如果请求已经正在运行,我们可以让它继续运行而不会中断
    2. 没有开启缓存
      • 如果请求失败,则重新启动请求,完成请求。
      • 如果请求已经正在运行,我们可以让它继续运行而不会中断
  • 缓存request

    target.setRequest(request);
    
  • 运行任务

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

    如果请求已经暂停,者清理请求,释放此请求所拥有的任何资源,显示当前占位图(如果已提供),并将该请求标记为已取消,然后将该请求添加到等待队列里。否则,加载占位图,开始运行任务。

    1. 请求已经完成
      获取结果,改变请求状态,通知。

      try {
          boolean anyListenerHandledUpdatingTarget = false;
          if (requestListeners != null) {
             for (RequestListener listener : requestListeners) {
                 anyListenerHandledUpdatingTarget |=
                    listener.onResourceReady(result, model, target, dataSource, isFirstResource);
            }
          }
          anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
      
          if (!anyListenerHandledUpdatingTarget) {
              Transition animation = animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation);
      }
      } finally {
        isCallingCallbacks = false;
      }
      notifyLoadSuccess();
      

      后面继续分析,这些回调里面都做了什么操作。

    2. 计算目标View尺寸
      一种是你使用了override() API为图片指定了固定的宽高,一种是没有指定。如果指定了就调用onSizeReady()方法。如果没指定就调用target.getSize()方法,并根据ImageView的layout_width和layout_height值来算出图片应该的宽高

      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
        onSizeReady(overrideWidth, overrideHeight);
      } else {
        // 通过目标view计算
        target.getSize(this);
      }
      
    3. 通知请求开始导入

      if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
          && canNotifyStatusChanged()) {
        target.onLoadStarted(getPlaceholderDrawable());
      }
      

请求执行引擎

这部分讲解真正的从网络端获取数据,并处理的逻辑

获取缓存数据

如果缓存存在,者直接返回缓存数据,不在执行请求,否则运行请求。

  • 构建缓存key
    缓存key和下面的参数有关
     EngineKey key =
         keyFactory.buildKey(
             model,
             signature,
             width,
             height,
             transformations,
             resourceClass,
             transcodeClass,
             options);
    
创建请求任务

请求任务也进行了缓存,如果缓存里面存在该请求任务,这不会创建新的请求任务,只添加执行回调。具体,请自己看源码。

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

如果缓存没有该任务,者会创建EngineJob和DecodeJob两个任务实例,然后缓存,执行任务

jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
执行请求任务
engineJob.start(decodeJob);

调用这个方法后会执行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);
    }
  }

根据不同的运行类型,执行不同的流程

  • 数据提取器生成器

    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);
     }
    }
    
  • 执行数据提取器生成器

     while (!isCancelled
         && currentGenerator != null
         && !(isStarted = currentGenerator.startNext())) {
       stage = getNextStage(stage);
       currentGenerator = getNextGenerator();
    
       if (stage == Stage.SOURCE) {
         reschedule();
         return;
       }
     }
    
  • 获取数据

    loadData.fetcher.loadData(helper.getPriority(), this);
    
    截屏2020-03-18下午4.51.07.png
    public void loadData(
        @NonNull Priority priority, @NonNull DataCallback callback) {
      long startTime = LogTime.getLogTime();
      try {
        InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
        callback.onDataReady(result);
      } catch (IOException e) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
          Log.d(TAG, "Failed to load data for url", e);
        }
        callback.onLoadFailed(e);
      } finally {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
          Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
        }
      }
    }
    
  • 读数据

    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);
      }
    }
    
  • 解析数据
    解析数据的逻辑可以自己去分析,比如需要的是bitmap,拿到数据流后,就会解析成bitmap,再进行一些转换,然后通知数据获取完成

    public Resource decode(
        DataRewinder rewinder,
        int width,
        int height,
        @NonNull Options options,
        DecodeCallback callback)
        throws GlideException {
      Resource decoded = decodeResource(rewinder, width, height, options);
      Resource transformed = callback.onResourceDecoded(decoded);
      return transcoder.transcode(transformed, options);
    }
    
    private void notifyComplete(Resource resource, DataSource dataSource) {
      setNotifiedOrThrow();
      callback.onResourceReady(resource, dataSource);
    }
    
  • 设置数据
    在设置数据之前,会根据参数配置处理好数据,然后设置给View

    1. 比如BitmapContainerTransitionFactory转换工厂
      @Override
      public boolean transition(R current, ViewAdapter adapter) {
          Resources resources = adapter.getView().getResources();
          Drawable currentBitmap = new BitmapDrawable(resources, getBitmap(current));
          return transition.transition(currentBitmap, adapter);
      }
      
    2. ImageViewTarget
      public void onResourceReady(@NonNull Z resource, @Nullable Transition transition) {
         if (transition == null || !transition.transition(resource, this)) {
         setResourceInternal(resource);
       } else {
         maybeUpdateAnimatable(resource);
       }
      }
      

总结

glide加载大体流程就这样,最后面数据解析和回调写得有点模糊,自己看了一下代码,有点多,自己可以去跟踪一下,看一下大体处理逻辑。下一篇将写一篇用法总结,如果对里面的原理不关心的,可以阅读我下一篇文章。

你可能感兴趣的:(【Glide】- 源码分析)