Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Glide:The loading process of the main flow #18

Open
yunshuipiao opened this issue May 17, 2019 · 0 comments
Open

Glide:The loading process of the main flow #18

yunshuipiao opened this issue May 17, 2019 · 0 comments

Comments

@yunshuipiao
Copy link
Owner

yunshuipiao commented May 17, 2019

Glide:The loading process of the main flow

[TOC]

分析来自 glide 4.8.0, 官方 使用文档

Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。

Glide 支持拉取,解码和展示视频快照,图片,和GIF动画。

Glide.with(fragment)
   .load(url)
   .into(imageView);

上面是官网介绍的最简单的用法,本文章就对这个主流程做个简单分析。

.with()

首先来看 with 方法做了什么:

 /*
 * @param context Any context, will not be retained.
 * @return A RequestManager for the top level application that can be used to start a load.
 * @see #with(android.app.Activity)
 * @see #with(android.app.Fragment)
 * @see #with(android.support.v4.app.Fragment)
 * @see #with(android.support.v4.app.FragmentActivity)
 */
@NonNull
public static RequestManager with(@NonNull Context context) {
  return getRetriever(context).get(context);
}

 @NonNull
 public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
 }
	
	// 最终调用
  @NonNull
  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();
  }

with 有多个重载方法,最终都会调用上述方法, 最终返回一个 RequestManager 对象。这里传入的 context 必须要绑定 view,或者 fragment.getActivity() 不等于 null 。

@NonNull
public static Glide get(@NonNull Context context) {
  if (glide == null) {
    synchronized (Glide.class) {
      if (glide == null) {
        // 初始化 glide
        checkAndInitializeGlide(context);
      }
    }
  }
  return glide;
}


  private static void checkAndInitializeGlide(@NonNull Context context) {
    // In the thread running initGlide(), one or more classes may call Glide.get(context).
    // Without this check, those calls could trigger infinite recursion.
    if (isInitializing) {
      throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
          + " use the provided Glide instance instead");
    }
    isInitializing = true;
    // 初始化
    initializeGlide(context);
    isInitializing = false;
  }

下面重点来看一下 初始化方法中具体做了什么:

private static void initializeGlide(@NonNull Context context) {
  initializeGlide(context, new GlideBuilder());
}

/**
 * A builder class for setting default structural classes for Glide to use.
 * 建造者模式创建 Glide
 */
public final class GlideBuilder {
  // 过渡方式
  private final Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions = new ArrayMap<>();
  // 负责加载和管理活跃和缓存的资源
  private Engine engine;
  // 重用的 bitmap 资源池
  private BitmapPool bitmapPool;
  // 不同数组类型的资源池
  private ArrayPool arrayPool;
  //	内存缓存
  private MemoryCache memoryCache;
  // 线程池,执行任务
  private GlideExecutor sourceExecutor;
  // 线程池,磁盘缓存
  private GlideExecutor diskCacheExecutor;
  // 懒加载创建磁盘缓存
  private DiskCache.Factory diskCacheFactory;
  // 缓存大小计算
  private MemorySizeCalculator memorySizeCalculator;
  // 网络连接监视器
  private ConnectivityMonitorFactory connectivityMonitorFactory;
  private int logLevel = Log.INFO;
  // 自定义独立的请求选项,常用
  private RequestOptions defaultRequestOptions = new RequestOptions();
  @Nullable
  // 创建 requestManager
  private RequestManagerFactory requestManagerFactory;
  private GlideExecutor animationExecutor;
  private boolean isActiveResourceRetentionAllowed;
  @Nullable
  // 当图片记载时,监测请求状态
  private List<RequestListener<Object>> defaultRequestListeners;
  private boolean isLoggingRequestOriginsEnabled;
  ...
}

所以知道 Glide类中的属性大概有些什么,其中重要的资源池,请求和缓存,后续会介绍。

回到初始化的逻辑,内容有点长,在关键部分做了注释。

private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
  Context applicationContext = context.getApplicationContext();
  // 是否通过注解生成了类: GeneratedAppGlideModuleImpl
  GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
  // 获取 meta 中定义的 GlideModule
  List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
  if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
    manifestModules = new ManifestParser(applicationContext).parse();
  }

  if (annotationGeneratedModule != null
      && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
    Set<Class<?>> excludedModuleClasses =
        annotationGeneratedModule.getExcludedModuleClasses();
    Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
    while (iterator.hasNext()) {
      com.bumptech.glide.module.GlideModule current = iterator.next();
      if (!excludedModuleClasses.contains(current.getClass())) {
        continue;
      }
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
      }
      iterator.remove();
    }
  }

  if (Log.isLoggable(TAG, Log.DEBUG)) {
    for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
      Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
    }
  }

  
  RequestManagerRetriever.RequestManagerFactory factory =
      annotationGeneratedModule != null
          ? annotationGeneratedModule.getRequestManagerFactory() : null;
  builder.setRequestManagerFactory(factory);
  for (com.bumptech.glide.module.GlideModule module : manifestModules) {
    module.applyOptions(applicationContext, builder);
  }
  if (annotationGeneratedModule != null) {
    annotationGeneratedModule.applyOptions(applicationContext, builder);
  }
  // 以上是 Generated API,编译时生成代码的部分,先跳过。
  
  // 默认值赋值,创建glide
  Glide glide = builder.build(applicationContext);
  for (com.bumptech.glide.module.GlideModule module : manifestModules) {
    try {
      module.registerComponents(applicationContext, glide, glide.registry);
    } catch (AbstractMethodError e) {
      throw new IllegalStateException(
          "Attempting to register a Glide v3 module. If you see this, you or one of your"
              + " dependencies may be including Glide v3 even though you're using Glide v4."
              + " You'll need to find and remove (or update) the offending dependency."
              + " The v3 module name is: " + module.getClass().getName(), e);
    }
  }
  if (annotationGeneratedModule != null) {
    annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
  }
  applicationContext.registerComponentCallbacks(glide);
  Glide.glide = glide;

上面的配置中,MemorySizeCalculator 内存大小的计算比较重要。至此,生成了 glide,用作后续处理的单例。

接着返回一个 requestManagerRetriever, 看一下glide 的属性,大部分都在 builder 中见过。

public class Glide implements ComponentCallbacks2 {
  private static final String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";
  private static final String TAG = "Glide";
  // 唯一单例
  private static volatile Glide glide;
  private static volatile boolean isInitializing;
	
  private final Engine engine;
  private final BitmapPool bitmapPool;
  private final MemoryCache memoryCache;
  // 提前填充如 bitmappool 的对象
  private final BitmapPreFiller bitmapPreFiller;
  private final GlideContext glideContext;
  // 管理组件注册,以扩展或替换Glide的默认加载、解码和编码逻辑。
  private final Registry registry;
  private final ArrayPool arrayPool;
  // 创建和销毁 RequestManger 的对象
  private final RequestManagerRetriever requestManagerRetriever;
  private final ConnectivityMonitorFactory connectivityMonitorFactory;
  private final List<RequestManager> managers = new ArrayList<>();
  private MemoryCategory memoryCategory = MemoryCategory.NORMAL;
  ...
}

继续往下,getRetriever(context).get(context);然后分别 get 不同对象获取 RequestManager。

@NonNull
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);
}
@NonNull
private RequestManager supportFragmentGet(
    @NonNull Context context,
    @NonNull FragmentManager fm,
    @Nullable Fragment parentHint,
    boolean isParentVisible) {
  SupportRequestManagerFragment current =
      getSupportRequestManagerFragment(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;
}

  @NonNull
  private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        // 创建无UI fragment 管理 glide 的生命周期
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        pendingSupportRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

这里创建了一个无UI的SupportRequestManagerFragment, 使其内部的 RequestManager 可以结合生命周期,安全的开始,停止和管理 glide 请求。这种方法在很多地方都有用到,不明白可以继续看一下源码。

小结

在上面的分析中,做的工作主要有:

各种属性的初始化工作,创建 glide 单例 —> 获取请求管理 RequestManager对象, 同时绑定 Lifecycle, start,stop,destory 事件。

有了 RequestManager 对象,那么接下来可以看一下 load 方法做了什么。

.load()

也是有多个重载方法,基本支持所有的图片资源加载,看下面的注释;

public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
  return asDrawable().load(bitmap);
}

public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
  return asDrawable().load(drawable);
}

public RequestBuilder<Drawable> load(@Nullable String string) {
  return asDrawable().load(string);
}

public RequestBuilder<Drawable> load(@Nullable Uri uri) {
  return asDrawable().load(uri);
}

public RequestBuilder<Drawable> load(@Nullable File file) {
  return asDrawable().load(file);
}

public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
  return asDrawable().load(resourceId);
}

public RequestBuilder<Drawable> load(@Nullable URL url) {
  return asDrawable().load(url);
}

public RequestBuilder<Drawable> load(@Nullable byte[] model) {
  return asDrawable().load(model);
}

public RequestBuilder<Drawable> load(@Nullable Object model) {
  return asDrawable().load(model);

通过调用 as(Drawable.class) 将传入的类型 decode 为 Drawable 对象。

@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
  return as(Drawable.class);
}

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

  @SuppressLint("CheckResult")
  @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
  protected RequestBuilder(
      @NonNull Glide glide,
      RequestManager requestManager,
      Class<TranscodeType> transcodeClass,
      Context context) {
    this.glide = glide;
    this.requestManager = requestManager;
    // 转换对象类型
    this.transcodeClass = transcodeClass;
    this.context = context;
    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
    this.glideContext = glide.getGlideContext();

    // 请求监听器
    initRequestListeners(requestManager.getDefaultRequestListeners());
    apply(requestManager.getDefaultRequestOptions());
  }

下面就开始 进入 load 步骤, 无论调用 load 的哪个重载方法,都会进入下面的代码:

@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
  // 加载对象赋值
  this.model = model;
  isModelSet = true;
  return this;
}

这一步将需要加载的 对象 赋值给RequesBuild 的 model 对象,接着就到了最复杂 into 步骤。

.into()

该方法接收一个 ImageView 对象,将要加载的图片在 view 上显示出来。

加载图片的过程分为几步,下面分别从每一步开始分析。

into() 也是有好几个重载方法,在拿到 ImageView 对象之后,会调用以下方法生成 target 对象,这里就是

DrawableImageViewTarget 对象。

/**
 * A factory responsible for producing the correct type of
 * {@link com.bumptech.glide.request.target.Target} for a given {@link android.view.View} subclass.
 */
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)");
    }
  }
}

接着经过 into 的几个重载方法,

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

第一阶段

在构建 新的请求中,最后会调用到此方法, 主要的注释如下:

 private Request buildRequestRecursive(
      Target<TranscodeType> target, // 上面提到的加载对象
      @Nullable RequestListener<TranscodeType> targetListener, //记载监听
      @Nullable RequestCoordinator parentCoordinator, // 协调多个请求
      TransitionOptions<?, ? super TranscodeType> transitionOptions, // 过渡
      Priority priority,  // 记载优先级
      int overrideWidth,
      int overrideHeight,
      BaseRequestOptions<?> requestOptions,  // 加载可选配置
      Executor callbackExecutor) {  // 线程池

    // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
   // 错误的请求选项,加载失败时显示,用法可配置。
    ErrorRequestCoordinator errorRequestCoordinator = null;
    if (errorBuilder != null) {
      errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
      parentCoordinator = errorRequestCoordinator;
    }
	
   	// 缩略图请求
    Request mainRequest =
        buildThumbnailRequestRecursive(
            target,
            targetListener,
            parentCoordinator,
            transitionOptions,
            priority,
            overrideWidth,
            overrideHeight,
            requestOptions,
            callbackExecutor);

    if (errorRequestCoordinator == null) {
      return mainRequest;
    }
		
   
    int errorOverrideWidth = errorBuilder.getOverrideWidth();
    int errorOverrideHeight = errorBuilder.getOverrideHeight();
    if (Util.isValidDimensions(overrideWidth, overrideHeight)
        && !errorBuilder.isValidOverride()) {
      errorOverrideWidth = requestOptions.getOverrideWidth();
      errorOverrideHeight = requestOptions.getOverrideHeight();
    }

    Request errorRequest =
        errorBuilder.buildRequestRecursive(
            target,
            targetListener,
            errorRequestCoordinator,
            errorBuilder.transitionOptions,
            errorBuilder.getPriority(),
            errorOverrideWidth,
            errorOverrideHeight,
            errorBuilder,
            callbackExecutor);
    errorRequestCoordinator.setRequests(mainRequest, errorRequest);
    return errorRequestCoordinator;
  }

上面的配置都是 glide 支持的配置,支持 error 图片和 缩略图 显示。同样,主流程没有设置,那么最终 创建一个 SingleRequest 对象。

private Request obtainRequest(...
  ) {
  return SingleRequest.obtain(...
}

到目前有了请求和target ,requestManager.track(target, request); 进行 生命周期管理,以及开始进行请求

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

  /**
   * Starts tracking the given request.
   */
  public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
      // singleRequest 开始请求, 见下分析
      request.begin();
    } else {
      request.clear();
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);
    }
  }
@Override
public synchronized void begin() {
  // 进行必要条件验证
  assertNotCallingCallbacks();
  stateVerifier.throwIfRecycled();
  startTime = LogTime.getLogTime();
  // 如果加载资源为空
  if (model == null) {
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      width = overrideWidth;
      height = overrideHeight;
    }
    // 作为备用的 drawable https://muyangmin.github.io/glide-docs-cn/doc/placeholders.html#%E5%90%8E%E5%A4%87%E5%9B%9E%E8%B0%83%E7%AC%A6fallback
    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;
  }

  // 等待确认图片加载大小
  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 对象.

该方法使用给定的参数开始加载,大概的流程如下:

检查当前使用的活跃资源,如果存在则使用;并且将新的不活跃的资源加入内存缓存;

如果内存缓存中存在,是直接使用;

检查正在加载的资源中是否存在,如果存在则返回;

否则开始新的加载。

:这里的活跃资源是指至少提供给一个请求并且还没有为回收的资源。如果所有使用者都释放了资源,将会进入缓存;这时如果从缓存中被重新使用,那么又会加入活跃资源中;如果资源从缓存中移除,那么资源会被重新利用,也可能被丢弃。没有严格要求释放资源,所以活跃的资源都很不稳定。

public synchronized <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;
	
  // 多个属性生成的,用于存储资源的key值
  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, callbackExecutor);
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Added to existing load", startTime, key);
    }
    return new LoadStatus(cb, current);
  }

  // 创建加载请求job
  EngineJob<R> engineJob =
      engineJobFactory.build(
          key,
          isMemoryCacheable,
          useUnlimitedSourceExecutorPool,
          useAnimationPool,
          onlyRetrieveFromCache);
	
  // 解码资源的 job
  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);

  if (VERBOSE_IS_LOGGABLE) {
    logWithTimeAndKey("Started new load", startTime, key);
  }
  return new LoadStatus(cb, engineJob);
}

上面方法中涉及两个类,一个是 DecodeJob、一个是 EngineJob。它们之间的关系是,EngineJob 内部维护了线程池,用来管理资源加载,已经当资源加载完毕的时候通知回调。 DecodeJob 继承了 Runnable,是线程池当中的一个任务。就像上面那样,我们通过调用 engineJob.start(decodeJob) 来开始资源加载。

####小结

在第一阶段中,整体还是比较清晰。 首先创建的 SingleRequest,然后 RequestMangenr 对 target 和 request 进行管理, 生命周期和加入请求队列;

接着开始 request,会调用 Engine.load() 方法来决定从活跃资源,或者缓存,或者新建 DecodeJob 中加载资源。

第二阶段

decodeJob 将会将会开始执行,并将结果回调给 cb。因为 DecodeJob 继承了 Runnable, 所以会执行其 run 方法。

engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);

  public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }

public void run() {
    // This should be much more fine grained, but since Java's thread pool implementation silently
    // swallows all otherwise fatal exceptions, this will at least make it obvious to developers
    // that something is failing.
    GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
    // Methods in the try statement can invalidate currentFetcher, so set a local variable here to
    // ensure that the fetcher is cleaned up either way.
    DataFetcher<?> localFetcher = currentFetcher;
    try {
      if (isCancelled) {
        notifyFailed();
        return;
      }
      runWrapped();
    } catch (CallbackException e) {
      // If a callback not controlled by Glide throws an exception, we should avoid the Glide
      // specific debug logic below.
      throw e;
    } catch (Throwable t) {
      // 主要过程在上面的 runWrapper(), 这里是为了捕获和处理 OOM 异常,图片操作不可避免。
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "DecodeJob threw unexpectedly"
            + ", isCancelled: " + isCancelled
            + ", stage: " + stage, t);
      }
     
      if (stage != Stage.ENCODE) {
        throwables.add(t);
        notifyFailed();
      }
      if (!isCancelled) {
        throw t;
      }
      throw t;
    } finally {
      // Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
      // close in all cases anyway.
      if (localFetcher != null) {
        localFetcher.cleanup();
      }
      GlideTrace.endSection();
    }
  

DecodeJob 的执行过程使用了状态模式,它会根据当前的状态决定将要执行的方法。在上面的方法中,当当前任务没有被取消的话,会进入到 runWrapped() 方法。该方法中会使用 runReason作为当前的状态决定要执行的逻辑:

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 是一个枚举类型,它包含的枚举值即为上面的三种类型。当我们在一个过程执行完毕之后会回调 DecodeJob 中的方法修改 runReason,然后根据新的状态值执行新的逻辑。

除了 runReasonDecodeJob 中还有一个变量 stage 也是用来决定 DecodeJob 状态的变量。同样,它也是一个枚举,用来表示将要加载数据的数据源以及数据的加载状态。它主要在加载数据的时候在 runGenerators()runWrapped()getNextStage() 三个方法中被修改。通常它的逻辑是,先从(大小、尺寸等)转换之后的缓存中拿数据,如果没有的话再从没有转换过的缓存中拿数据,最后还是拿不到的话就从原始的数据源中加载数据。

以上就是 DecodeJob 中的状态模式运行的原理。

在上面的逻辑中,从 INITIALIZE 状态开始,由于这是没有设计到缓存,所以 得到 SourceGenerator,

currentGenerator.startNext())
  
@Override
  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      // 加入cache
      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;
  }

由于这是加载的是网络上的资源,默认情况下会使用 HttpUrlFetcher 下载数据:

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();
  // 获得流,防止资源泄漏
  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);
  }

小结

这里涉及到 DecodeJob 的执行,和网络的请求过程,代码量相对较大的部分。因此也只是一个简单的分析,多看源码,多思考。下一阶段是将输入流转换成 Drawable。

第三阶段

上面的阶段结束,会回调 onDataReady() 方法:

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

glide 有默认磁盘缓存策略,所以这里会执行:cb.reschedule();, 最后又回到 DecodeJob 的run 方法。

@Override
public void reschedule() {
  runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
  callback.reschedule(this);
}

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

这里的主要逻辑是构建一个用于将数据缓存到磁盘上面的 DataCacheGeneratorDataCacheGenerator 的流程基本与 SourceGenerator 一致,也就是根据资源文件的类型找到 ModelLoader,然后使用 DataFetcher 加载缓存的资源。与之前不同的是,这次是用 DataFecher 来加载 File 类型的资源。也就是说,当我们从网络中拿到了数据之后 Glide 会先将其缓存到磁盘上面,然后再从磁盘上面读取图片并将其显示到控件上面。所以,当从网络打开了输入流之后 SourceGenerator 的任务基本结束了,而后的显示的任务都由 DataCacheGenerator 来完成。

DataCacheGenerator 中使用 DataUrlLoader.loadData() 后,回调 onDataReady() 方法,接着会调用到:

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

这一阶段执行完毕,主要是 获取输入流之后的 处理过程,到达下一阶段。

第四阶段

notifyEncodeAndRelease(resource, currentDataSource);

方法一路往下追,最后回到SingleRequest,

private synchronized void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
  
    .......
      .......
      .........
    if (!anyListenerHandledUpdatingTarget) {
      Transition<? super R> animation =
          animationFactory.build(dataSource, isFirstResource);
      // target 加载显示图片
      target.onResourceReady(result, animation);
    }
  } finally {
    isCallingCallbacks = false;
  }
	
  // 通知结束
  notifyLoadSuccess();
}

然后 回调 ImageViewTarget 的方法, 整个过程结束。

  @Override
  public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
  }
  
    @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }

总结

以上就是 Glide.with(fragment).into(ImageView) 的主流程的分析,大致步骤就是初始化 glide,创建 RequestManager, 接着创建出加载的类型 RequestBuild<Drawable>model 对象。

into 过程中,主要有以下几步:

第一个阶段是开启 DecodeJob 的过程。DecodeJob 负责从缓存或者从原始的数据源中加载图片资源,对图片进行变换和转码,是 Glide 图片加载过程的核心。DecodeJob 继承了 Runnable,实际进行图片加载的时候会将其放置到线程池当中执行。这个阶段我们重点介绍的是从 RequestBuilder 构建一个 DecodeJob 并开启 DecodeJob 任务的过程。即构建一个 DecodeJob 并将其丢到线程池里的过程。

第二个阶段是打开网络流的过程。这个阶段会根据我们的图片资源来从数据源中加载图片数据。以我们的示例为例,在默认情况下会从网络当中加载图片,并得到一个 InputStream.

第三个阶段是将输入流转换为 Drawable 的过程。得到了 InputStream 之后还要调用 BitmapFactorydecodeStream() 方法来从 InputStream 中得到一个 Drawable.

第四个阶段是将 Drawable 显示到 ImageView 上面的过程。

其中最重要并且最负责的是中间两步,这篇文章只是提了个大概,后续会详细的分析。

当然,关于 Glide 还有很多没有谈到, 包括可配置的 RequestOptions,缓存,注解时生成的部分,希望后面有时间慢慢从用法到源码,慢慢了解吧。

参考文章

官方中文文档翻译

Glide 系列-2:主流程源码分析(4.8.0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant