亲宝软件园·资讯

展开

Android图片加载框架Gilde源码层深入分析

lpf_wei 人气:0

1.使用Gilde显示一张图片

Glide.with(this).
        load("https://cn.bing.com/sa/simg/hpb/xxx.jpg")
        .into(imageView);

上边是Glide最简单的来显示一张图片,虽然只有三步操作:with、load、into,但是gilde却通过大量的代码在维护着。

with:返回一个RequestManager

load:返回一个RequestBuilder

下面通过源码分析这三块,重点是Glide生明周期的管理和缓存的使用,以及整个加载的流程的梳理。

2.Glide with操作源码解析

Glide的with函数由于参数类型原因有多个重载,下面选择传入Activity当做参数的

public static RequestManager with(@NonNull Activity activity) {
  return getRetriever(activity).get(activity);
}

这个方法就是得到一个RequestManager

分析getRetriever

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  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();
}

得到一个RequestManagerRetriever,这个管理RequestManager

RequestManagerRetriever的get方法

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
        // Only unwrap a ContextWrapper if the baseContext has a non-null application context.
        // Context#createPackageContext may return a Context without an Application instance,
        // in which case a ContextWrapper may be used to attach one.
        && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
      return get(((ContextWrapper) context).getBaseContext());
    }
  }
  return getApplicationManager(context);
}

根据不同的参数类型,执行不同的代码逻辑。

参数是:FragmentActivity的源码

public RequestManager get(@NonNull FragmentActivity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity);
    frameWaiter.registerSelf(activity);
    FragmentManager fm = activity.getSupportFragmentManager();
    return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

注意这里判断了线程,如果是子线程会直接走ApplicationContext类型的函数重载。

supportFragmentGet方法:

private RequestManager supportFragmentGet(
    @NonNull Context context,
    @NonNull FragmentManager fm,
    @Nullable Fragment parentHint,
    boolean isParentVisible) {
  SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
  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);
    // This is a bit of hack, we're going to start the RequestManager, but not the
    // corresponding Lifecycle. It's safe to start the RequestManager, but starting the
    // Lifecycle might trigger memory leaks. See b/154405040
    if (isParentVisible) {
      requestManager.onStart();
    }
    current.setRequestManager(requestManager);
  }
  return requestManager;
}

这个方法会创建一个空白的SupportRequestManagerFragment,这个Fragment的作用很重要,也是Glide的核心,声明周期的管理就是靠这个来监控的。

监控了:onStart、onStop、onDestroy,所以Glide在使用的时候,我们不用关心资源的释放,因为Glide会自动释放资源,释放资源的依据就是这个声明周期的管理。

get函数参数是:ApplicationContext 的源码

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) {
        Glide glide = Glide.get(context.getApplicationContext());
        applicationManager =
            factory.build(
                glide,
                new ApplicationLifecycle(),
                new EmptyRequestManagerTreeNode(),
                context.getApplicationContext());
      }
    }
  }
  return applicationManager;
}

可见当参数类型是:ApplicationContext或者子线程调用with方法不会创建空白的Fragmnet来进行生命周期的监控,也不会进行资源释放,所以在调用with方法的时候不能传ApplicationContext。

下面分析Glide分别在onStart、onStop、onDestroy执行了那些逻辑

RequestManager的onStart方法

public synchronized void onStart() {
  resumeRequests();
  targetTracker.onStart();
}

执行了刷新请求和Tracker的onStart,resumeRequests这个跟请求的缓存的使用

RequestTracker 的resumeRequests方法

public void resumeRequests() {
  isPaused = false;
  for (Request request : Util.getSnapshot(requests)) {
    if (!request.isComplete() && !request.isRunning()) {
      request.begin();
    }
  }
  pendingRequests.clear();
}

将运行队列requests所有没有完成又处于停止状态的任务全部开始执行,requests是一个正在运行的请求缓存集合,这是一个set集合,处于等待状态的请求队列pendingRequests进行清理操作。这个就是Glide感应到声明周期onStart进行的操作。

RequestManager的onStop方法

public synchronized void onStop() {
  pauseRequests();
  targetTracker.onStop();
}

RequestTracker的pauseRequests方法

public void pauseRequests() {
  isPaused = true;
  for (Request request : Util.getSnapshot(requests)) {
    if (request.isRunning()) {
      request.pause();
      pendingRequests.add(request);
    }
  }
}

request.pause():将所有的运行缓存集合中的请求进行暂停操作,并将这些请求加入到等待缓存请求集合pendingRequests

RequestManager的onDestroy

public synchronized void onDestroy() {
  targetTracker.onDestroy();
  for (Target<?> target : targetTracker.getAll()) {
    clear(target);
  }
  targetTracker.clear();
  requestTracker.clearRequests();
  lifecycle.removeListener(this);
  lifecycle.removeListener(connectivityMonitor);
  Util.removeCallbacksOnUiThread(addSelfToLifecycle);
  glide.unregisterRequestManager(this);
}

可以看到Glide在感应到onDestroy会进行各种资源释放,监听的移除操作。

3.Glide 的load操作

RequestManager的load方法

public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
  return asDrawable().load(bitmap);
}
public RequestBuilder<Drawable> asDrawable() {
   return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
    @NonNull Class<ResourceType> resourceClass) {
   return new RequestBuilder<>(glide, this, resourceClass, context);
}

load方法返回值是RequestBuilder,通过new的方式进行创建

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

initRequestListeners:初始化监听

apply:校验参数

load的过程比较简单,重点的into将会非常复杂

4.Glide的into流程解析

public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
  return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
<Y extends Target<TranscodeType>> Y into(
    @NonNull Y target,
    @Nullable RequestListener<TranscodeType> targetListener,
    Executor callbackExecutor) {
    return into(target, targetListener, /*options=*/ this, callbackExecutor);
 }

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)) {
    if (!Preconditions.checkNotNull(previous).isRunning()) {
      previous.begin();
    }
    return target;
  }
  requestManager.clear(target);
  target.setRequest(request);
  requestManager.track(target, request);
  return target;
}

buildRequest 会通过SingleRequest.obtain,创建一个SingleRequest,这个必须要前置了解,后边的流程需要用到。

requestManager.track(target, request):这个是主流程,下面来看RequestManager的track方法

RequestManager的track方法

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

RequestTracker的runRequest方法

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

这里的Request是一个接口,调用了begin方法,这个Request就是前面创建的SingleRequest

SingleRequest的begin

public void begin() {
  synchronized (requestLock) {
    ...
    if (status == Status.COMPLETE) {
      onResourceReady(
          resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
      return;
    }
    cookie = GlideTrace.beginSectionAsync(TAG);
    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));
    }
  }
}

如果没有设置宽高会调用 target.getSize(this)来获取大小,如果设置了会执行onSizeReady方法

SingleRequest的onSizeReady方法

public void onSizeReady(int width, int height) {
  stateVerifier.throwIfRecycled();
  synchronized (requestLock) {
    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,
            callbackExecutor);
    if (status != Status.RUNNING) {
      loadStatus = null;
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
  }
}

接下来调用Engin的load方法

Engin的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;
  EngineKey key =
      keyFactory.buildKey(
          model,
          signature,
          width,
          height,
          transformations,
          resourceClass,
          transcodeClass,
          options);
  EngineResource<?> memoryResource;
  synchronized (this) {
    memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
    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);
    }
  }
  // Avoid calling back while holding the engine lock, doing so makes it easier for callers to
  // deadlock.
  cb.onResourceReady(
      memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
  return null;
}

loadFromMemory这个方法是先从缓存中去查找,如果有直接就可以直接使用了

loadFromMemory方法

@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:从活动缓存查找,如果有直接返回使用,这个缓存是一个Map集合

loadFromCache:从内存缓存查找,如果有直接使用,这个是一个LRU集合

上边两个是都是内存缓存,只要显示在页面上的,都会缓存在活动缓存。

Engin的waitForExistingOrStartNewJob方法

private <R> LoadStatus waitForExistingOrStartNewJob(
    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,
    EngineKey key,
    long startTime) {
  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<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);
  if (VERBOSE_IS_LOGGABLE) {
    logWithTimeAndKey("Started new load", startTime, key);
  }
  return new LoadStatus(cb, engineJob);
}

engineJob 管理一个线程池,decodeJob这个是任务的抽象是一个Runable,所以下面就会执行任务的run方法

DecodeJob的run方法

public void run() {
  ...
  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) {
    ..
  } 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方法

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

默认会执行case INITIALIZE的runGenerators(),getNextGenerator得到的是SourceGenerator,这个必须要明确,否则走不下去。

SourceGenerator的startNext方法

public boolean startNext() {
  ...
  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;
      startNextLoad(loadData);
    }
  }
  return started;
}

DecodeHelper的getLoadData方法

List<LoadData<?>> getLoadData() {
  if (!isLoadDataSet) {
    isLoadDataSet = true;
    loadData.clear();
    List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
    //noinspection ForLoopReplaceableByForEach to improve perf
    for (int i = 0, size = modelLoaders.size(); i < size; i++) {
      ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
      LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
      if (current != null) {
        loadData.add(current);
      }
    }
  }
  return loadData;
}

HttpGlideUrlLoader的buildLoadData方法

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的loadData

SourceGenerator的startNextLoad方法

private void startNextLoad(final LoadData<?> toStart) {
  loadData.fetcher.loadData(
      helper.getPriority(),
      new DataCallback<Object>() {
        @Override
        public void onDataReady(@Nullable Object data) {
          if (isCurrentRequest(toStart)) {
            onDataReadyInternal(toStart, data);
          }
        }
        @Override
        public void onLoadFailed(@NonNull Exception e) {
          if (isCurrentRequest(toStart)) {
            onLoadFailedInternal(toStart, e);
          }
        }
      });
}

loadData.fetcher的fetcher就是HttpUrlFetcher

HttpUrlFetcher的loadData方法

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方法

private InputStream loadDataWithRedirects(
    URL url, int redirects, URL lastUrl, Map<String, String> headers) throws HttpException {
  if (redirects >= MAXIMUM_REDIRECTS) {
    throw new HttpException(
        "Too many (> " + MAXIMUM_REDIRECTS + ") redirects!", INVALID_STATUS_CODE);
  } 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", INVALID_STATUS_CODE);
      }
    } catch (URISyntaxException e) {
      // Do nothing, this is best effort.
    }
  }
  urlConnection = buildAndConfigureConnection(url, headers);
  try {
    // 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();
  } catch (IOException e) {
    throw new HttpException(
        "Failed to connect or obtain data", getHttpStatusCodeOrInvalid(urlConnection), e);
  }
  ...
}

到这里就看到urlConnection拿到图片的InputStream,终于从一个网络链接请求到流了,然后就是将流转成bitmap显示的过程了,感兴趣的可以继续查看。

5.Glide

声明周期和缓存总结 Glide 调用with函数如果传入的ApplicationContext或者在子线程不会为我们创建空白的Fragment,不会进行生命周期的监控,也就不会进行资源自动释放,所以不用传ApplicationContext。其他情况glide会自动创建空白的Fragment来进行生命周期监控,并自动进行资源释放。

Glide的缓存分为4级缓存:活动缓存、内存缓存、磁盘缓存、网络缓存

加载全部内容

相关教程
猜你喜欢
用户评论