Android-Glide详解二

目录

一,load源码分析

二,into源码分析

[2.1 构建一个Request对象](#2.1 构建一个Request对象)

[2.2 缓存检测](#2.2 缓存检测)

[2.3 如果缓存中没有,构建新的异步任务](#2.3 如果缓存中没有,构建新的异步任务)

[2.4 解压inputstream 采样压缩 得到bitmap 转换为Drawable](#2.4 解压inputstream 采样压缩 得到bitmap 转换为Drawable)

[2.5 构建磁盘缓存 内存缓存](#2.5 构建磁盘缓存 内存缓存)

[2.6 显示图片](#2.6 显示图片)


关于glide的使用及with部分源码分析可以参考文章Android-Glide详解-CSDN博客

一,load源码分析

下面我们先来看下load的具体代码:

Kotlin 复制代码
val requestBulider:RequestBuilder<Drawable> =requestManager.load(url)
java 复制代码
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
  return asDrawable().load(bitmap);
}

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

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

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

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

@SuppressWarnings("deprecation")
@NonNull
@CheckResult
@Override
public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
  return asDrawable().load(resourceId);
}

@SuppressWarnings("deprecation")
@CheckResult
@Override
@Deprecated
public RequestBuilder<Drawable> load(@Nullable URL url) {
  return asDrawable().load(url);
}

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

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

从上面可以看出,load可以加载多种类型的对象。

先看下asDrawable():

java 复制代码
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
  return as(Drawable.class);
}

继续往下看as:

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

这里可以看出 实例化了一个RequestBuilder。然后返回来继续看asDrawable().load(string)方法:

java 复制代码
@NonNull
@Override
@CheckResult
public RequestBuilder<TranscodeType> load(@Nullable String string) {
  return loadGeneric(string);
}

然后看看loadGenericv方法:

java 复制代码
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
  //加载的数据源
  this.model = model;
  //这个请求是否已经添加了加载的数据源
  isModelSet = true;
  return this;
}

到此为止,RequestBuilder就构建完成了,load的使命也完成了。

二,into源码分析

into是Glide里面最为复杂的部分,要想详细的分析完整,一时半会是不可能的,在这里我只能大致的分析下into的流程,不一点一点的分析了,因为它是过程很复杂,但是比较好理解,不像rxjava那样绕来绕去,所以一点一点的分析没有意义。

2.1 构建一个Request对象

因为load已经构建了一个RequestBuilder对象,所以在它的into方法里我们看到:

java 复制代码
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);
  Request previous = target.getRequest();
  。。。
    return target;
  }

通过buildRequest构建了一个Request对象,具体看看这个方法都构造了什么参数:

java 复制代码
private Request buildRequest(
    Target<TranscodeType> target,
    @Nullable RequestListener<TranscodeType> targetListener,
    BaseRequestOptions<?> requestOptions,
    Executor callbackExecutor) {
  return buildRequestRecursive(
      /*requestLock=*/ new Object(),
      target,
      targetListener,
      /*parentCoordinator=*/ null,
      transitionOptions,
      requestOptions.getPriority(),
      requestOptions.getOverrideWidth(),
      requestOptions.getOverrideHeight(),
      requestOptions,
      callbackExecutor);
}

有请求的宽,高,采样等等

继续往下看,发现最终走到了:

java 复制代码
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对象。

2.2 缓存检测

在SingleRequest中,它会调用Engine的load方法:

java 复制代码
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;
//拿到缓存或者请求的 key
  EngineKey key =
      keyFactory.buildKey(
          model,
          signature,
          width,
          height,
          transformations,
          resourceClass,
          transcodeClass,
          options);
  EngineResource<?> memoryResource;
  synchronized (this) {
//根据 key 拿到活动缓存或者内存缓存中的资源
    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);
    }
  }
//如果 ActiveResources 活动缓存中有就回调出去
  cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
  return null;
}
java 复制代码
@Nullable
private EngineResource<?> loadFromMemory(
    EngineKey key, boolean isMemoryCacheable, long startTime) {
  if (!isMemoryCacheable) {
    return null;
  }
//根据key从活动缓存中拿资源
  EngineResource<?> active = loadFromActiveResources(key);
  if (active != null) {
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from active resources", startTime, key);
    }
    return active;
  }
//活动缓存没有 就从LruResourceCache 中找寻这个资源
  EngineResource<?> cached = loadFromCache(key);
  if (cached != null) {
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from cache", startTime, key);
    }
    return cached;
  }
  return null;
}
java 复制代码
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) {
//根据 Key 看看缓存中是否正在执行
  EngineJob<?> current = jobs.get(key, onlyRetrieveFromCach
  if (current != null) {
//如果正在执行,把数据回调出去
    current.addCallback(cb, callbackExecutor);
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Added to existing load", startTime
    }
    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);
//把当前需要执行的 key 添加进缓存
  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);
}

通过 engine.load 这个函数里面的逻辑,可以总结3点:

  1. 先构建请求或者缓存 KEY ;

  2. 根据 KEY 从内存缓存中查找对应的资源数据(ActiveResources(活动缓存,内部 是一个 Map 用弱引用持有),LruResourceCache),如果有就回调 对应监听的 onResourceReady 表示数据准备好了。

  3. 从执行缓存中查找对应 key 的任务

  4. 如果找到了,就说明已经正在执行了,不用重复执行。

  5. 没有找到,通过 EngineJob.start 开启一个新的请求任务执行。

2.3 如果缓存中没有,构建新的异步任务

通过上面代码可以看出,异步任务是通过engineJob.start方法开始执行的:

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

它里面是通过一个线程池来执行的,具体的runnable任务就是decodeJob的run方法:

java 复制代码
@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) {
    
    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 {
    
    if (localFetcher != null) {
      localFetcher.cleanup();
    }
    GlideTrace.endSection();
  }
}

然后接着看看runWrapped()方法:

java 复制代码
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);
  }
}
java 复制代码
private Stage getNextStage(Stage current) {
  switch (current) {
    case INITIALIZE:
//如果外部调用配置了资源缓存策略,那么返回 Stage.RESOURCE_CACHE
//否则继续调用 Stage.RESOURCE_CACHE 执行
      return diskCacheStrategy.decodeCachedResource()
          ? Stage.RESOURCE_CACHE
          : getNextStage(Stage.RESOURCE_CACHE);
    case RESOURCE_CACHE:
//如果外部配置了源数据缓存,那么返回 Stage.DATA_CACHE
//否则继续调用 getNextStage(Stage.DATA_CACHE)
      return diskCacheStrategy.decodeCachedData()
          ? Stage.DATA_CACHE
          : getNextStage(Stage.DATA_CACHE);
    case DATA_CACHE:
      //如果只能从缓存中获取数据,则直接返回 FINISHED,否则,返回SOURCE。
      return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
    case SOURCE:
    case FINISHED:
      return Stage.FINISHED;
    default:
      throw new IllegalArgumentException("Unrecognized stage: " + current);
  }
}

然后我们再来看看run中的这个方法:

java 复制代码
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);
  }
}

因为我们什么都没有配置,所以我们的资源执行器就是SourceGenerator:

然后我们就可以直接看下他的startNext方法:

java 复制代码
@Override
public boolean startNext() {
  if (dataToCache != null) {
    Object data = dataToCache;
    dataToCache = null;
    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;
      startNextLoad(loadData);
    }
  }
  return started;
}

获取一个 ModelLoad 加载器,使用加载器中的 fetcher 根据优先级加载数据

走到DecoderHelp的buildLoadData:

java 复制代码
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;
}
java 复制代码
@Nullable
LoadData<Data> buildLoadData(
    @NonNull Model model, int width, int height, @NonNull Options options);

boolean handles(@NonNull Model model);

下面是它的实现类 :

最终我们会在Httpurifetcher中找到它的网络请求,返回了InputStream

java 复制代码
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();
  // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
  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);
  }

2.4 解压inputstream 采样压缩 得到bitmap 转换为Drawable

在DecodeJob中解析网络请求回来的数据:

java 复制代码
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 {
// 调用 decodeFrom 解析数据HttpUrlFetcher , InputStream ,currentDataSource
    resource = decodeFromData(currentFetcher, currentData, currentDataSource);
  } catch (GlideException e) {
    e.setLoggingDetails(currentAttemptingKey, currentDataSource);
    throwables.add(e);
  }
  if (resource != null) {
//解析完成后,下发通知
    notifyEncodeAndRelease(resource, currentDataSource);
  } else {
    runGenerators();
  }
}

具体怎么解析的,这里就不详细展开了,内容太庞大,大致步骤为:

1:deResource 将源数据解析成资源(源数据: InputStream, 中间产物: Bitmap)

2:调用 DecodeCallback.onResourceDecoded 处理资源

3:调用 ResourceTranscoder.transcode 将资源转为目标资源(目标资源类型: Drawable)

2.5 构建磁盘缓存 内存缓存

我们来看看下发通知的函数notifyEncodeAndRelease:

java 复制代码
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
  if (resource instanceof Initializable) {
    ((Initializable) resource).initialize();
  }
  Resource<R> result = resource;
  LockedResource<R> lockedResource = null;
  if (deferredEncodeManager.hasResourceToEncode()) {
    lockedResource = LockedResource.obtain(resource);
    result = lockedResource;
  }
//通知调用层数据已经装备好了
  notifyComplete(result, dataSource);
  stage = Stage.ENCODE;
  try {
//将资源存到磁盘缓存
    if (deferredEncodeManager.hasResourceToEncode()) {
      deferredEncodeManager.encode(diskCacheProvider, options);
    }
  } finally {
    if (lockedResource != null) {
      lockedResource.unlock();
    }
  }
  // Call onEncodeComplete outside the finally block so that it's not called if the encode process
  // throws.
  onEncodeComplete();
}

这里就是将解析回来的资源存到了磁盘缓存。

然后通知上层来展示数据。

java 复制代码
@SuppressWarnings("unchecked")
@Override
public synchronized void onEngineJobComplete(
  EngineJob<?> engineJob, Key key, EngineResource<?>
  resource) {
  if (resource != null) {
  resource.setResourceListener(key, this);
//收到下游返回回来的资源,添加到活动缓存中
  if (resource.isCacheable()) {
   activeResources.activate(key, resource);
  }
 }
 jobs.removeIfCurrent(key, engineJob);
}

然后添加到活动缓存中。

2.6 显示图片

当一切准备就绪的时候,最终会回到ImageViewTarget中去显示图片:

java 复制代码
private void setResourceInternal(@Nullable Z resource) {
  // Order matters here. Set the resource first to make sure that the Drawable has a valid and
  //显示资源
  setResource(resource);
  maybeUpdateAnimatable(resource);
}
相关推荐
花追雨8 小时前
Android -- 双屏异显之方法一
android·双屏异显
小趴菜82278 小时前
安卓 自定义矢量图片控件 - 支持属性修改矢量图路径颜色
android
氤氲息8 小时前
Android v4和v7冲突
android
KdanMin8 小时前
高通Android 12 Launcher应用名称太长显示完整
android
chenjk48 小时前
Android不可擦除分区写文件恢复出厂设置,无法读写问题
android
袁震8 小时前
Android-Glide缓存机制
android·缓存·移动开发·glide
工程师老罗8 小时前
Android笔试面试题AI答之SQLite(2)
android·jvm·sqlite
User_undefined9 小时前
uniapp Native.js 调用安卓arr原生service
android·javascript·uni-app
安小牛9 小时前
android anr 处理
android
刘争Stanley12 小时前
如何高效调试复杂布局?Layout Inspector 的 Toggle Deep Inspect 完全解析
android·kotlin·android 15·黑屏闪屏白屏