Glide

73 阅读5分钟

Glide继承自ComponentCallbacks2,用来监听内存情况。在内存紧张的时候,系统会自动触发ComponentCallbacks2trimMemory回调。

public void trimMemory(int level) {
  memoryCache.trimMemory(level);
  bitmapPool.trimMemory(level);
  arrayPool.trimMemory(level);
}

问题1:Glide里面创建Fragment的时候为什么要用HashMap临时保存创建好的Fragment

final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments =
    new HashMap<>();
private SupportRequestManagerFragment getSupportRequestManagerFragment(
    @NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
  SupportRequestManagerFragment current = pendingSupportRequestManagerFragments.get(fm);
  if (current == null) {
    current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = new SupportRequestManagerFragment();
      current.setParentFragmentHint(parentHint);
      pendingSupportRequestManagerFragments.put(fm, current);
      fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
      handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
    }
  }
  return current;
}

原因是为了防止重复添加多个Fragment。比如下面这代码:

void glide(){
    Glide.with(context).load(url).into(view);
    Glide.with(context).load(url).into(view);
}

当连续调用Glide.with的时候,会多次创建对应的fragment。但是现在他两的生命周期是一样的,所以没必要创建多个。然后由于执行代码:

fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();

这是一个异步操作,就导致只用 fm.findFragmentByTag(FRAGMENT_TAG);来获取对应的Fragment对象是不准确的,所以需要临时用HashMap保存下,他是同步的。


Glide.with(FullscreenActivity.this)
    .load(list.get(cun))
    .into(view);
  • Glide.with作用:创建单例对象Glide。根据当前的生命周期对象创建对应的RequestManger对象
  • Glide.with.load作用:创建RequestBuilder对象。每掉用一次load就会创建一个RequestBuilder对象
    RequestManager里面有两个变量,RequsetTrackerTargetTracker,RequestTracker用来保存调用load的时候创建的Request对象。TragetTracker用来保存每次load的时候创建的target对象。为什么要保存呢,因为Glide是具有生命周期的,可以在RequestManager里面直接管理他们。

接着看Glide.with.load方法,这个方法这里一定要看懂才行。

private <Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    BaseRequestOptions<?> options,
    Executor callbackExecutor) {
  Preconditions.checkNotNull(target);
  Request request = buildRequest(target, targetListener, options, callbackExecutor); // 2
  Request previous = target.getRequest();    //  1

  if (request.isEquivalentTo(previous)
      && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {  // 3
    if (!Preconditions.checkNotNull(previous).isRunning()) {
      previous.begin();
    }
    return target;
  }
  requestManager.clear(target); // 4
  target.setRequest(request); // 6
  requestManager.track(target, request);  // 5

  return target;
}

先来看标记1的地方:

// 这是他的源码
private Object getTag() {
  return view.getTag(tagId);
}

再来看下标记6的地方:

private void setTag(Object tag) {
  isTagUsedAtLeastOnce = true;
  view.setTag(tagId, tag);
}

可以看到Glide将创建的Request对象添加到了View里面。然后在通过get方法获取当前view上一次添加的request对象。
在看标记3的地方你就懂了,他对通一个view进行了request的判断,如果和上次的请求是同一个request的话就不要在重新执行额外的添加操作,可以直接开始requset请求。
如果不是同一个request的话就要看标记4 和 标记5的地方。
标记4:移除当前的target和request对象,因为你这个view现在需要开启一个新的图片请求了,上一个请求没有完成的话也不必在继续请求下去了,现在就要停止下来。
标记5:添加现在新的Request对象,和Target对象。

关于RequsetBuild创建Request对象的过程是,判断当前view有没有已经存在的Request对象,如果没有或者请求内容修改就创建新的requet,并且终止上一次的请求,立马开启这次的请求。


在load的时候去获取图片大小,然后调用异步的网络请求,然后在调用target.onLoadStarted去设置占位图片。

现在看下是怎么加载出来的。

@Override
public void onSizeReady(int width, int height) {
  stateVerifier.throwIfRecycled();
  synchronized (requestLock) {
    if (status != Status.WAITING_FOR_SIZE) {
      return;
    }
    status = Status.RUNNING;

    float sizeMultiplier = requestOptions.getSizeMultiplier();
    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
    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是在什么时候创建的。

private Request obtainRequest(
    Object requestLock,
    Target<TranscodeType> target,
    RequestListener<TranscodeType> targetListener,
    BaseRequestOptions<?> requestOptions,
    RequestCoordinator requestCoordinator,
    TransitionOptions<?, ? super TranscodeType> transitionOptions,
    Priority priority,
    int overrideWidth,
    int overrideHeight,
    Executor callbackExecutor) {
  return SingleRequest.obtain(
      context,
      glideContext,
      requestLock,
      model,
      transcodeClass,
      requestOptions,
      overrideWidth,
      overrideHeight,
      priority,
      target,
      targetListener,
      requestListeners,
      requestCoordinator,
      glideContext.getEngine(),
      transitionOptions.getTransitionFactory(),
      callbackExecutor);
}

可以看到在创建SingleRequest对象的时候通过glideContext.getEngine()创建的。那GlideContext是怎么创建的?答案是在创建RequestBuilder创建的时候创建的:

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();
  apply(requestManager.getDefaultRequestOptions());
}

RequestBuilder又是在我们调用Glide.with().load里面有个as方法创建的。最终这个GlideContext其实就是Glide对象里面创建的:

Glide(
    @NonNull Context context,
    @NonNull Engine engine,
    @NonNull MemoryCache memoryCache,
    @NonNull BitmapPool bitmapPool,
    @NonNull ArrayPool arrayPool,
    @NonNull RequestManagerRetriever requestManagerRetriever,
    @NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
    int logLevel,
    @NonNull RequestOptionsFactory defaultRequestOptionsFactory,
    @NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
    @NonNull List<RequestListener<Object>> defaultRequestListeners,
    @NonNull List<GlideModule> manifestModules,
    @Nullable AppGlideModule annotationGeneratedModule,
    @NonNull GlideExperiments experiments) {

glideContext =
      new GlideContext(
          context,
          arrayPool,
          registry,
          imageViewTargetFactory,
          defaultRequestOptionsFactory,
          defaultTransitionOptions,
          defaultRequestListeners,
          engine,
          experiments,
          logLevel);
}

目前可以知道了Glide全局只有一个对象,GlideContext全局只有一个对象,然后Engine也只有一个对象。


看下Engine.load方法,主要做数据请求的是DecodeJob里面的run方法:

class DecodeJob<R>
    implements DataFetcherGenerator.FetcherReadyCallback,
        Runnable,
        Comparable<DecodeJob<?>>,
        Poolable {
    public void 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);
  }
}

runWrapped方法很关键,他用来查询可以处理的处理器:

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

处理器有三种:

  1. ResourceCacheGenerator 用来处理磁盘缓存中获取的经过转化后的资源
  2. DataCacheGenerator 用来处理磁盘里面的原图
  3. SourceGenerator 用来下载网络图

1和2也就是我们经常说的磁盘缓存。1保存的是经过transform也就是经过变换过的资源。2保存的是未被处理过的图片。也就是原图。
最终通过3进行下载。


Glide缓存有三种:

  1. 活动缓存
  2. 内存缓存
  3. 磁盘缓存

当一个图片已经被其他View在使用的时候,那么这个资源就会被缓存到活动缓存里面,其他view要用的话就直接拿这个活动缓存里面的就行。
活动缓存是通过hashMap的方式进行存储的,键值是弱引用,当触发gc的时候,活动缓存里面的数据会完全被移除,然后添加到内存缓存里面。


总结Glide整个加载流程: 当调用Glide.into的时候,会去创建Request对象。每个用来展示的视图他会保存上一次创建好的Request对象。这个时候就会比较当前的Request对象和当前视图曾经保存的request是一样的就那以前的request进行展示。如果不一样就开启请求。engine.load()。
每次load的时候都会创建key对象。然后通过这个key去找对应的缓存数据。首先从活动缓存拿,然后再从内存缓存里面找:

class Engine{
    private EngineResource<?> loadFromMemory(
        EngineKey key, boolean isMemoryCacheable, long startTime) {
      if (!isMemoryCacheable) {
        return null;
      }

      EngineResource<?> active = loadFromActiveResources(key);
      if (active != null) {
        return active;
      }

      EngineResource<?> cached = loadFromCache(key);
      if (cached != null) {
        return cached;
      }

      return null;
    }
}

活动缓存的内容,已经用专门的一篇博客进行了分析。内存缓存LruResourceCache在另外一篇文章进行了详解。