博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【笔记整理】Glide 4.9.0 关于数据加载之后的回调过程
阅读量:3565 次
发布时间:2019-05-20

本文共 8223 字,大约阅读时间需要 27 分钟。

当 Glide 从网络加载原始的数据的时候,会来到 HttpUrlFetcher#loadData() 方法,在 中说过,当加载完成后,会通过 callback.onDataReady() 方法将结果回传,最终会回溯到 DecodeJob#onDataFetcherReady 这个方法中,下面将会回溯的具体流程进行分析。

// HttpUrlFetcher.javapublic void loadData(    @NonNull Priority priority, @NonNull DataCallback
callback) {
long startTime = LogTime.getLogTime(); try {
// 获取网络图片, 内部使用了 HttpURLConnection 实现, 仅仅做了重定向的处理 InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); // 回调 callback.onDataReady() 方法将结果回传, // callback 是在调用方法时传递过来的,这里即 SourceGenerator#startNext() 方法中传递的,即 SourceGenerator 对象自身 callback.onDataReady(result); } catch (IOException e) {
callback.onLoadFailed(e); } finally {
... }}

HttpUrlFetcher#loadData() 中得到加载的数据的 InputStream 之后,会将其传入回调方法进行处理。

其中 callback 是在调用方法时传递进来的。而方法是在 SourceGenerator#startNext() 进行调用的。

// SourceGenerator.javapublic boolean startNext() {
... sourceCacheGenerator = null; loadData = null; boolean started = false; while (!started && hasNextModelLoader()) {
// 1. 从 DecodeHelper 的数据加载集合中, 获取一个数据加载器 loadData = helper.getLoadData().get(loadDataListIndex++); if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true; // 2. 使用加载器中 fetcher 执行数据加载 // 加载网络的 url 资源对应的就是 HttpGlideUrlLoader, // 它对应的 ModelLoader.LoadData 中的 fetcher 为 HttpUrlFetcher 类型 loadData.fetcher.loadData(helper.getPriority(), this); } } return started;}

可以看到看到传递的 callback 即为 SourceGenerator 对象自身(其实现了 DataCallback 接口)。

// SourceGenerator.javapublic void onDataReady(Object data) {
... if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
// 将数据赋值给 dataToCache dataToCache = data; // We might be being called back on someone else's thread. Before doing anything, // we should reschedule to get back onto Glide's thread. // 数据加载回调 onDataReady() 是在子线程中,应该切换回 Glide 的线程。 // 进一步回调,cb 是在 new SourceGenerator 时传递过来的,为 DecodeJob 对象 cb.reschedule(); } else {
... }}// 注意 FetcherReadyCallback#reschedule() 的目的就是为了请求在 Glide 所属的线程// 中再次调用 DataFetcherGenerator#startNext() 方法,// DataFetcherGenerator 即为 SourceGenerator 的父类。interface FetcherReadyCallback {
/** * Requests that we call startNext() again on a Glide owned thread. */ void reschedule(); ...}

SourceGenerator#cb 是在初始化的时候传递过来的。更具体的,是在 DecodeJob#getNextGenerator() 方法中创建时传递进来的。

// DecodeJob.javaprivate DataFetcherGenerator getNextGenerator() {
switch (stage) {
... case SOURCE: // 对应加载的图片的 Generator return new SourceGenerator(decodeHelper, this); ... }}public void reschedule() {
// 将 runReason 状态更新为 RunReason.SWITCH_TO_SOURCE_SERVICE runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; // callback 则是在 `DecodeJob#init()` 时从外部传递进来的对应的 EngineJob 对象 callback.reschedule(this);}

可以看到 new SourceGenerator 时第二参数传递的是 DecodeJob 对象自身(其实现了 FetcherReadyCallback 接口)。

因此 SourceGenerator#onDataReady() 中执行 cb.reschedule() 实际上是执行 DecodeJob#reschedule()

然后又会进一步调用 callback.reschedule(this)callback 则是在 DecodeJob#init() 时从外部传递进来的,实际上为对应的 EngineJob 对象。

具体是在 Engine#load() 中,当内存缓存中没有获取到目标资源时,就会进一步从磁盘或者网络获取资源。此时就会构建 EngineJob 与 DecodeJob 对象。

因此在 DecodeJob#reschedule() 又会进一步回调 EngineJob#reschedule()

// EngineJob.javapublic void reschedule(DecodeJob
job) {
getActiveSourceExecutor().execute(job);}

getActiveSourceExecutor() 会获得对应的 GlideExecutor,调用 GlideExecutor#execute() 实际上就是使用 GlideExecutor 内部的线程池来处理 RunnableDecodeJob 实现了 Runnable 接口)。

从而实现了前面(在 SourceGenerator#onDataReady() 方法中)说的从加载资源的子线程切换到 Glide 线程。

因此,此时又会执行 DecodeJob#run() 方法,进而执行 DecodeJob#runWrapped() 方法。

// DecodeJob.javaprivate 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); }}// 非静态内部类private enum RunReason {
/** The first time we've been submitted. */ INITIALIZE, /** * We want to switch from the disk cache service to the source executor. */ SWITCH_TO_SOURCE_SERVICE, /** * We retrieved some data on a thread we don't own and want to switch back to our thread to * process the data. */ DECODE_DATA,}

此时 runReason 已经变为 SWITCH_TO_SOURCE_SERVICE 了(在回调 DecodeJob#reschedule() 被赋值更新的)。

因此此时会在 GlideExecutor 对应的线程池中去执行 runGenerators(),进而调用 currentGenerator.startNext()。从而实现 FetcherReadyCallback#reschedule() 的目的,即切换回 Glide 线程再次调用 startNext()


又回到 SourceGenerator#startNext()

// SourceGenerator.javapublic boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache; dataToCache = null; cacheData(data); } if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true; } sourceCacheGenerator = null; ...}// 将数据缓存到磁盘中private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime(); try {
// 这里的 encoder 实际上为 StreamEncoder(在 Glide 的构造方法中注册的,用于处理 InputStream 类型的数据的) Encoder encoder = helper.getSourceEncoder(dataToCache); DataCacheWriter writer = new DataCacheWriter<>(encoder, dataToCache, helper.getOptions()); originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature()); // 缓存到磁盘中 helper.getDiskCache().put(originalKey, writer); } finally {
loadData.fetcher.cleanup(); } // 为 sourceCacheGenerator 赋值为 DataCacheGenerator 对象 sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);}

在前面的 SourceGenerator#onDataReady() 被回调的时候,会将加载的数据赋值给 dataToCache,即 dataToCache 此时不为空,因此进入到 if 语句中。

cacheData() 方法中,会将加载的数据缓存到磁盘中,且会为 sourceCacheGenerator 赋值新的 DataCacheGenerator 对象。

因此对于后面的 if 语句,sourceCacheGenerator 不会为空。进入到 sourceCacheGenerator.startNext() 中,即 DataCacheGenerator#startNext() 中。

if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;}
// DataCacheGenerator.javapublic boolean startNext() {
... loadData = null; boolean started = false; while (!started && hasNextModelLoader()) {
ModelLoader
modelLoader = modelLoaders.get(modelLoaderIndex++); loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions()); if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true; // 这里的 fetcher 为 FileFetcher,调用其 loadData() 的时候又会把自身传递过去 loadData.fetcher.loadData(helper.getPriority(), this); } } return started;}

注意,在调用 FileFetcher#loadData() 的时候,会把 DataCacheGenerator 对象自身传递过去,因为其实现了 DataFetcher.DataCallback 接口。

// FileFetcher.javapublic void loadData(@NonNull Priority priority, 	@NonNull DataCallback
callback) {
try {
data = opener.open(file); } catch (FileNotFoundException e) {
callback.onLoadFailed(e); return; } callback.onDataReady(data);}

根据 File file 得到 data,又会回调 DataCacheGenerator#onDataReady()

// DataCacheGenerator.javapublic void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);}

而成员变量 DataCacheGenerator#cb 则是通过 DataCacheGenerator 的构造方法传递进来的,具体是在 SourceGenerator#cacheData() 中传递的,实际上就是 SourceGenerator 对象自身。

// SourceGenerator.javapublic 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);}

SourceGenerator#cb 在前面也说过,同样是在 new 的时候传递进来的,即 DecodeJob 对象,因此最终会回调搭到 DecodeJob#onDataFetcherReady()

注意,到目前为止,是在前面切换到 GlideExecutor 的线程池的线程中来执行的。

到这里,就是从 HttpUrlFetcher#loadData() 最终回溯到 DecodeJob#onDataFetcherReady() 的具体流程。

转载地址:http://brerj.baihongyu.com/

你可能感兴趣的文章
php开启redis扩展包与redis安装
查看>>
php使用openssl来实现非对称加密
查看>>
pdo如何防止 sql注入
查看>>
myisam和innodb的区别
查看>>
MySQL建表规范与注意事项(个人精华)
查看>>
JDK8接口的新特性
查看>>
synchronized的局限性与lock的好处
查看>>
redis和memcached有什么区别?
查看>>
Spring中的设计模式
查看>>
如何设计一个秒杀系统 -- 架构原则
查看>>
如何设计一个秒杀系统 -- 动静分离方案
查看>>
JWT 快速了解
查看>>
实习日志一
查看>>
Springboot读取自定义配置文件的几种方法
查看>>
ZigbeeCC2530 --(裸机和协议栈)串口时钟配置
查看>>
ZigBee开发环境搭建 ----IAR for 8051与SmartRFProgram等软件安装使用
查看>>
Python ---太空射击游戏
查看>>
C/C++之struct的小知识
查看>>
温湿度传感器(AM2312)
查看>>
PTA 数据结构与算法题目集(中文)7-47 打印选课学生名单(25 分)vector容器
查看>>