Glide缓存机制源码解读(二)---网络图片下载及缓存过程

Glide缓存机制源码解读(二)


篇章目标要点

Glide是目前最为流行的图片加载框架,内部提供了缓存机制,本文系列目的是记录学习Glide缓存机制要点。缓存最主要的点就是读,写,控制,本文就是围绕这几个方面进行解读。目的是通过向源码学习形成自己的能力进度和思考。目前已经有很多的文章都有撰写相应的学习笔记,部分笔记存在的问题时,所摘取的代码片段未标记代码来源,所以对照源码阅读时,有时找不到位置,本文介绍方法时,会注明相应的类的路径。在第一篇文章中已经介绍了Gilde使用了哪些缓存框架实现的内存缓存和硬盘缓存,完成了下图0.缓存对象构建。本篇作为第二篇文章,将完整的讲述的Glide加载网络图片的流程和原理(下图1.加载外部图片),加载的数据缓存的流程和原理(下图2.写入缓存),访问外部数据前使用缓存数据的流程(下图3.缓存读取)。
缓存关注点


提示:以下是本篇文章正文内容,下面案例可供参考

一、源码获取

可以在github下载Glide源码进行阅读,通过git工具下载

git clone https://github.com/bumptech/glide.git

二、请求网络图片使用示例

Glide可以非常便捷的实现网络请求图片且设置缓存的功能,简要的示例代码如下

RequestOptions options = RequestOptions.bitmapTransform(new RoundedCorners(20))
        .diskCacheStrategy(DiskCacheStrategy.DATA)//设置缓存原始数据
        .override(150,200);
Glide.with(imageview)//imageview为待填充图片的view对象
        .asBitmap()
        .load(url)//url为待加载的网络图片的url链接
        .apply(options)
        .into(target);

三、网络图片的下载流程

在上文示例中注入了图片的url链接之后,Glide框架内部是如何下载图片的呢。在此我梳理了调用的流程和关键的代码,增加了相应的注释,为了便于通过阅读快速了解调用的前后流程。相关过程我放在了以下图片当中,调用流程参照下图
网络图片下载流程
其内部是使用HttpUrlConnection进行的网络图片加载,相应的源码如下
源码位置

com.bumptech.glide.load.data.HttpUrlFetcher

网络请求过程代码

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);
}
……
//构建网络请求参数
private HttpURLConnection buildAndConfigureConnection(URL url, Map<String, String> headers)
    throws HttpException {
  HttpURLConnection urlConnection;
  try {
    urlConnection = connectionFactory.build(url);
  } catch (IOException e) {
    throw new HttpException("URL.openConnection threw", /*statusCode=*/ 0, e);
  }
  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);
  return urlConnection;
}

四、写入缓存流程

在下载流程中第10步SourceGenerator类中方法void startNextLoad(…)执行完图片加载流程之后,就会进入到写入缓存的流程。在DecodeJob中将网络图片的InputStream转为Resource数据类型后,会分别执行内存缓存流程和硬盘缓存流程,详细流程如下
写入缓存流程
写入缓存部分的关键代码如下
源码位置

com.bumptech.glide.load.engine.DecodeJob

在网络数据获取完毕之后,会在onDataFetcherReady中回调,然后执行decodeFromRetrievedData()进入执行缓存写入流程

//网络数据请求完毕
@Override
public void onDataFetcherReady(
    Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
  this.currentSourceKey = sourceKey;
  this.currentData = data;
  this.currentFetcher = fetcher;
  this.currentDataSource = dataSource;
  this.currentAttemptingKey = attemptedKey;
  this.isLoadingFromAlternateCacheKey = sourceKey != decodeHelper.getCacheKeys().get(0);

  if (Thread.currentThread() != currentThread) {
    runReason = RunReason.DECODE_DATA;
    callback.reschedule(this);
  } else {
    GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
    try {
      //对取回的数据进行解码
      decodeFromRetrievedData();
    } finally {
      GlideTrace.endSection();
    }
  }
}
……
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, isLoadingFromAlternateCacheKey);
  } else {
    runGenerators();
  }
}
……
//通知编码并释放资源
private void notifyEncodeAndRelease(
    Resource<R> resource, DataSource dataSource, boolean isLoadedFromAlternateCacheKey) {
  GlideTrace.beginSection("DecodeJob.notifyEncodeAndRelease");
  try {
    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, isLoadedFromAlternateCacheKey);

    stage = Stage.ENCODE;
    //执行内存缓存流程,基于DiskCache缓存数据至硬盘
    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();
  } finally {
    GlideTrace.endSection();
  }
}

五、下载前缓存使用流程

在调用Glide方法时可以设置DiskCacheStrategy.DATA缓存未转换的原始数据,设置DiskCacheStrategy.RESOURCE缓存转换后的处理数据,设置DiskCacheStrategy.ALL缓存转换前后的数据。其代码实现流程如下图所示
下载前读取缓存流程
其主要过程是在DecodeJob类中实现,通过方法getNextGenerator()执行从缓存中读取数据的流程,核心代码如下
源码位置

com.bumptech.glide.load.engine.DecodeJob

执行读取缓存数据的代码

private void runWrapped() {
  switch (runReason) {
    case INITIALIZE:
      //1.确认任务类型
      stage = getNextStage(Stage.INITIALIZE);
      //2.创建任务执行器
      currentGenerator = getNextGenerator();
      //3.执行任务
      runGenerators();
      break;
    case SWITCH_TO_SOURCE_SERVICE:
      runGenerators();
      break;
    case DECODE_DATA:
      decodeFromRetrievedData();
      break;
    default:
      throw new IllegalStateException("Unrecognized run reason: " + runReason);
  }
}
……
//2.创建任务执行器
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);
  }
}
……
private void runGenerators() {
  currentThread = Thread.currentThread();
  startFetchTime = LogTime.getLogTime();
  boolean isStarted = false;
  while (!isCancelled
      && currentGenerator != null
      && !(isStarted = currentGenerator.startNext())) {//3.执行任务
    //1.确认任务类型
    stage = getNextStage(stage);
    //2.创建任务执行器
    currentGenerator = getNextGenerator();

    if (stage == Stage.SOURCE) {
      reschedule();
      return;
    }
  }
  // We've run out of stages and generators, give up.
  if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
    notifyFailed();
  }

  // Otherwise a generator started a new load and we expect to be called back in
  // onDataFetcherReady.
}

六、学习心得

通过完成第二章之后,已经完全梳理清楚了Glide缓存机制的使用流程。下一章节重点阐述Glide关于缓存的控制机制,包括缓存大小控制和缓存有效期控制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值