Android 开发之Glide《三》

前言

前面一章我们介绍了Glide 下载图片的过程,我们本章梳理下载图片之后回调的过程

一、回调

在这里插入图片描述

loadData 获取到数据流之后通知回调,数据就绪。
此处的callbakc 实际指向的是SourceGenerator。
所以看看SourceGenerator对象的onDataReady方法都做了什么

private final FetcherReadyCallback cb; //成员变量
@Override
public void onDataReady(Object data) {
  DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
  if (data != null && diskCacheStrategy.isDataCacheable(
loadData.fetcher.getDataSource())) {
   //需要缓存到磁盘
  //保存网络图片输入流到dataToCache 
    dataToCache = data;
//重新规划图片加载
    cb.reschedule();
  } else {
//不需要缓存到磁盘
    cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
        loadData.fetcher.getDataSource(), originalKey);
  }
}

一般情况下默认是缓存到磁盘的,所有我们看cb.reschedule的源码,这个cb 是FetcherReadyCallback 对象,它实际指向的是DecodeJob。
在这里插入图片描述

DecodeJob

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

这里的callback 实际是EngineJob
在这里插入图片描述

接着又进入了DecodeJob的run方法,根据上一篇对DecodeJob run方法的分析,它会进入runWrapped的SWITCH_TO_SOURCE_SERVICE分支,SWITCH_TO_SOURCE_SERVICE分支直接调用runGenerators,而当前的图片执行器还是网络执行器SourceGenerator,接着又执行SourceGenerator的startNext方法:

public boolean startNext() {
    if (dataToCache != null) {
      //dataToCache就是网络请求的网络图片输入流,此时已经不是null 了
      Object data = dataToCache;
      dataToCache = null;
      //缓存网络图片输入流到磁盘中
      cacheData(data);
    }
    //执行原始图片磁盘缓存输入流执行器
    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    //...
}

绕了一大圈又回到SourceGenerator的startNext,在startNext中调用cacheData对网络图片输入流进行缓存:

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());
      //创建原始图片缓存key
      originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
      //缓存到磁盘中
      helper.getDiskCache().put(originalKey, writer);
    } finally {
      loadData.fetcher.cleanup();
    }
    //创建原始图片磁盘缓存执行器
    sourceCacheGenerator =new DataCacheGenerator(Collections.
singletonList(loadData.sourceKey), helper, this);
  }

具体怎么缓存的大家参考下面这篇文章[Glide缓存](https://www.jianshu.com/p/3612a96459ec)

当DataCacheGenerator缓存玩图片之后会调用SourceGenerator的onDataFetcherReady

```java
  @Override
  public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {
    // This data fetcher will be loading from a File and provide the wrong data source, so override
    // with the data source of the original fetcher
    cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
  }

上面说过此处的cb 实际是DecodeJob

```java
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
    DataSource dataSource, Key attemptedKey) {
  this.currentSourceKey = sourceKey;
//将数据交给DecodeJob的currentData持有
  this.currentData = data;
  this.currentFetcher = fetcher;
  this.currentDataSource = dataSource;
  this.currentAttemptingKey = attemptedKey;
  if (Thread.currentThread() != currentThread) {
  //修改runReason
    runReason = RunReason.DECODE_DATA;
    callback.reschedule(this);
  } else {
    TraceCompat.beginSection("DecodeJob.decodeFromRetrievedData");
    try {
//省略部分代码
      decodeFromRetrievedData();
    } finally {
      TraceCompat.endSection();
    }
  }

callback.reschedule(this) 会开启一个新的线程进入runWrapper 进而调用decodeFromRetrievedData,大家可以结合上一篇文章一起看,我们这里直接分析decodeFromRetrievedData 方法。

private void decodeFromRetrievedData() {

  Resource<R> resource = null;
  try {
 //1、将Inpustream转换成Resource对象
    resource = decodeFromData(currentFetcher, currentData, currentDataSource);
  } catch (GlideException e) {
    e.setLoggingDetails(currentAttemptingKey, currentDataSource);
    throwables.add(e);
  }
  if (resource != null) {
//2、将数据继续传递
    notifyEncodeAndRelease(resource, currentDataSource);
  } else {
    runGenerators();
  }
}

上面的方法做了两件事:
1、将图片数据InputStream流转换成Resource对象
2、调用notifyEncodeAndRelease将数据继续传递。
此时我们的图片数据由InputStream转换成了Resource对象。接下来分析notifyEncodeAndRelease都做了写什么:

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

我们来到了DecodeJob类的notifyComplete方法:

private void notifyComplete(Resource<R> resource, DataSource dataSource) {
  setNotifiedOrThrow();
//又是callback
  callback.onResourceReady(resource, dataSource);
}

notifyComplete方法中我们见到了又一个callback,并且调用其onResourceReady方法,这个callback 实际指向的是EngineJob,所以看看EngineJob的onResourceReady方法:

@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
  //原始数据
  this.resource = resource;
  this.dataSource = dataSource;
  MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}

finally,见到了Handler的身影,说明我们距离终点不远了,上文的handler发送了MSG_COMPLETE消息该handler来处理,


public boolean handleMessage(Message message) {
  EngineJob<?> job = (EngineJob<?>) message.obj;
  switch (message.what) {
    case MSG_COMPLETE:
      job.handleResultOnMainThread();

仅仅是调用了EngineJob的handleResultOnMainThread方法,此时我们已经将图片数据传递到了UI线程:

@Synthetic
void handleResultOnMainThread() {

//将数据转换成engineResource
  engineResource = engineResourceFactory.build(resource, isCacheable);
  hasResource = true;

  engineResource.acquire();
  listener.onEngineJobComplete(this, key, engineResource);

  //noinspection ForLoopReplaceableByForEach to improve perf
  for (int i = 0, size = cbs.size(); i < size; i++) {
    ResourceCallback cb = cbs.get(i);
    if (!isInIgnoredCallbacks(cb)) {
      engineResource.acquire();
 	//又是一个callback
      cb.onResourceReady(engineResource, dataSource);
    }
  }
  // Our request is complete, so we can release the resource.
  engineResource.release();

  release(false /*isRemovedFromQueue*/);
}

该方法做了两件事:
1、将resource赋值给engineResource
2、调用callback

此处的callback 是ResourceCallback,实际指向的是SingleRequest
在这里插入图片描述
所以我们进入该SingleReqeust的onResourceReady方法:

@Override
public void onResourceReady(Resource<?> resource, DataSource dataSource) {

//省略部分逻辑
  onResourceReady((Resource<R>) resource, (R) received, dataSource);
}

 *
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
  // We must call isFirstReadyResource before setting status.
  boolean isFirstResource = isFirstReadyResource();
  status = Status.COMPLETE;
  this.resource = resource;

  isCallingCallbacks = true;
  try {
    if ((requestListener == null
        || !requestListener.onResourceReady(result, model, target, 
dataSource, isFirstResource))
        && (targetListener == null
        || !targetListener.onResourceReady(result, model, target, dataSource, isFirstResource))) {
      Transition<? super R> animation =
          animationFactory.build(dataSource, isFirstResource);
 //最终调用target的onResourceReady
      target.onResourceReady(result, animation);
    }
  } finally {
    isCallingCallbacks = false;
  }

  notifyLoadSuccess();
}

此处的target就是DrawableImageViewTarget,该target的onResourceReady方法的最总逻辑就是调用:
imageView.setImageDrawable(resource);

来完成图片的展示。
到此为止,本篇分析完毕,分析源码的过程中四个callback来回调度可把我绕坏了。

总结

至此,关于Glide 下载完图片之后缓存图片与回调显示图片的过程就梳理完了。虽然过程梳理完了,但是别人为什么这么写,为什么这么架构,还需要细细品味。

Android图片框架Glide-3.7.0(最新,很强大),超好用的图片框架(包含jar和源码) Glide 是一个高效、开源、 Android设备上的媒体管理框架,它遵循BSD、MIT以及Apache 2.0协议发布。Glide具有获取、解码和展示视频剧照、图片、动画等功能,它还有灵活的API,这些API使开发者能够将Glide应用在几乎任何网络协议栈里。创建Glide的主要目的有两个,一个是实现平滑的图片列表滚动效果,另一个是支持远程图片的获取、大小调整和展示。近日,Glide 3.0发布,现已提供 jar包下载 ,同时还支持使用Gradle以及Maven进行构建。该版本包括很多值得关注的新功能,如支持Gif 动画和视频剧照解码、智能的暂停和重新开始请求、支持缩略图等,具体新增功能如下如下: GIF 动画的解码 :通过调用Glide.with(context).load(“图片路径“)方法,GIF动画图片可以自动显示为动画效果。如果想有更多的控制,还可以使用Glide.with(context).load(“图片路径“).asBitmap()方法加载静态图片,使用Glide.with(context).load(“图片路径“).asGif()方法加载动画图片 本地视频剧照的解码: 通过调用Glide.with(context).load(“图片路径“)方法,Glide能够支持Android设备中的所有视频剧照的加载和展示 缩略图的支持: 为了减少在同一个view组件里同时加载多张图片的时间,可以调用Glide.with(context).load(“图片路径“).thumbnail(“缩略比例“).into(“view组件“)方法加载一个缩略图,还可以控制thumbnail()中的参数的大小,以控制显示不同比例大小的缩略图 Activity 生命周期的集成: 当Activity暂停和重启时,Glide能够做到智能的暂停和重新开始请求,并且当Android设备的连接状态变化时,所有失败的请求能够自动重新请求 转码的支持: Glide的toBytes() 和transcode() 两个方法可以用来获取、解码和变换背景图片,并且transcode() 方法还能够改变图片的样式 动画的支持: 新增支持图片的淡入淡出动画效果(调用crossFade()方法)和查看动画的属性的功能 OkHttp 和Volley 的支持: 默认选择HttpUrlConnection作为网络协议栈,还可以选择OkHttp和Volley作为网络协议栈 其他功能: 如在图片加载过程中,使用Drawables对象作为占位符、图片请求的优化、图片的宽度和高度可重新设定、缩略图和原图的缓存等功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值