本文主要总结Glide使用过程中下列问题
- Glide的资源管理
- Glide中一次资源请求过程 阅读本文需要%分钟
创建Glide
glide对象的创建采用了Builder模式,以默认建造为例,代码如下:
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock(),
defaultTransitionOptions);
Glide对象创建主要涉及四个部分,分别是执行器相关、缓存相关、引擎相关和请求管理相关,下面分别解释下这个部分的默认配置:
1.1 三类执行器
- sourceExecutor:该执行器是包装了线程池的GlideExecutor对象,其线程池核心数为最大可以cpu数,存活时间为0,拒绝策略为默认拒绝策略(超出队列限制会直接扔出拒绝的异常)。本执行器主要负责处理远程资源请求、缓存和解码
- diskCacheExecutor:该执行器也是包装了线程池的GlideExecutor对象,不同的是其线程池核心数和最大线程数为1,是一个单线程的线程池。其主要负责处理加载本地缓存资源和对本地资源进行编解码
- animationExecutor:该执行器也是包装了线程池的GlideExecutor对象,不同的是其线程池核心数为0,最大线程数为2或者1(依据cpu选择)。
1.2 三类缓存相关
- bitmapPool:该pool是一个缓存bitmap的内存缓存,有两种实现LruBitmapPool和BitmapPoolAdapter,较常用的LruBitmapPool基于LRU实现,大小为4或者1个屏幕截图打大小(Android7.0以下为4,以上为1)
- arrayPoo:也是个内存缓存,大小为4Mb(低内存设备为2Mb),主要用作读写数据流时的缓冲区
- memoryCache:内存缓存,大小为2张屏幕截图大小(这里有个前提是bitmapPool和memoryCache的内存和必须小于设定的内存阈值),主要缓存那些未被View使用的Resource
1.3 引擎相关
- engine:负责开始加载资源、管理资源(激活资源和缓存资源),触发加载资源是通过EngineJob和DecodeJob来驱动的,每一个DecodeJob代表了业务层一次资源请求在Engine的任务映射。管理资源主要是针对Resource类型的资源状态(缓存,释放,使用等)
1.4 请求管理
- requestManagerRetriever:请求管理(RequestManager)的跟踪工具(全局唯一),其职责是根据不同的Context类型创建具有不同生命周期的RequestManager对象来管理每一个资源请求的证明周期
- RequestManager:负责请求的跟踪管理,生命周期跟随创建时Context。其能暂停、开始、恢复资源请求。也能感知Context的生命周期,从而自动管理请求。
请求资源过程
请求资源过程涉及的配置较多,这里就采用最简单的配置来请求,所有的配置就是默认配置。
Glide.with(this).load(url).into(imageView);
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
可以看到with方法通过在RequestManagerRetriever对象上调用get方法来获取一个RequestManager,而requestManagerRetriever对象是唯一的,其get方法根据线程作出处理
-
如果在子线程调用,则使用Application作为参数,调用get重载方法获取RequestManager,该RequestManager能感受Application的生命周期
-
如果是主线程,则使用fragmentGet方法来获取RequestManager
private RequestManager fragmentGet(@NonNull Context context, @NonNull android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint, boolean isParentVisible) { RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible); 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); current.setRequestManager(requestManager); } return requestManager; }
fragmentGet方法中会获取一个RequestManagerFragment,其继承Fragment,会被添加到Activity中,负责感受生命周期并通知给Glide中的监听生命周期的组件,比如RequestManager。这也是为什么Glide的请求能自动感受生命周期的原因。至此我们完成了获取RequestManager的步骤。接下来就是调用ReuqestManager的load方法
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
protected RequestBuilder(Glide glide, RequestManager requestManager,
Class<TranscodeType> transcodeClass, Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.defaultRequestOptions = requestManager.getDefaultRequestOptions();
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.requestOptions = defaultRequestOptions;
this.glideContext = glide.getGlideContext();
}
load方法分为两步,第一步是asDrawable方法,其具体工作是创建一个RequestBuilder对象,设定其ResourceType为Drawable.class,也就是资源消费方需要什么类型的资源,这里是ImageView需要Drawable类型的资源。第二部为RequestBuilder的load方法
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
load方法就是设置RequestBuild的mode为String类型的url,这里的mode是业务方提供的资源配置信息,Glide在加载的时候会根据mode的内容和对象类型去获取对应的model加载器,这个后面会说到。 现在已经创建了一个RequestBuilder对象,接下来就是调用其into方法了。这样调用方就任务就完成了,后面的就是Glide框去处理资源了。
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
RequestOptions requestOptions = this.requestOptions;
...//省略scaleType相关处理
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
into方法分为两步
-
1.调用了buildImageViewTarget方法构建了ViewTarget,传递的transcodeClass参数就是之前设定的Drawable.class,最终会创建一个持有当前ImageView对象的DrawableImageViewTarget对象。这里为啥要用ViewTarget给ImageView对象呢?个人理解是为了统一封装接口,便于管理,因为Viewtarget是一个桥梁。
- 作为资源消费者,能感受资源的生命周期变化,从而作出适当的Ui展示,比如资源开始加载(onLoadStarted())时展示占位符、资源加载成功(onResourceReady())时展示资源、资源被清除(onLoadCleared())时重置资源等。
- 作为View的包装对象,能被TargetTracker根据宿主生命周期统一管理View的状态,同时能操作View来获取View的宽高等属性
-
2.调用了其重载的into方法
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, @NonNull RequestOptions options) { Util.assertMainThread(); Preconditions.checkNotNull(target); if (!isModelSet) { throw new IllegalArgumentException("You must call #load() before calling #into()"); } options = options.autoClone(); Request request = buildRequest(target, targetListener, options); Request previous = target.getRequest(); if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { request.recycle(); if (!Preconditions.checkNotNull(previous).isRunning()) { previous.begin(); } return target; } requestManager.clear(target); target.setRequest(request); requestManager.track(target, request); return target; }
重载的into方法主要做了四件事
-
创建Request对象,这里会创建SingleRequest对象
-
清理target对象上的请求和资源,比如之前这个target上展示了第一个资源,这里会释放该资源
-
绑定target和request对象,绑定是通过View的setTag方式来实现的
-
委托requestManager去管理和触发请求,这里会将target加入到TargetTracker对象中,将request加入RequestTracker对象内部集合中并触发请求,
public void runRequest(@NonNull Request request) { requests.add(request);//加入集合管理 if (!isPaused) { //开始请求 request.begin(); } else { request.clear(); pendingRequests.add(request); } }
这里的request对象是SingleRequest类型,所以看一下该对象的begin方法做了什么
public void begin() {
...//省略无关代码
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
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());
}
}
begin方法主要是根据status来做不同处理,而status一共有6种状态,分别对应的含义为:
| Status | 含义 |
|---|---|
| PENDING | 默认状态,表示当前请求被加入队列,但还未开始 |
| RUNNING | 表示当前请求正在进行中,同一个请求不能被执行两次 |
| WAITING_FOR_SIZE | 表示当前请求正在等待尺寸确定 |
| COMPLETE | 表示当前请求已经完成 |
| FAILED | 请求失败了 |
| CLEARED | 请求已经被占位符清除了 |
begin方法处理如下
-
1.如果该请求已完成,则直接回调onResourceReady方法,否则进入步骤2
-
2.设置当前状态为WAITING_FOR_SIZE,检验当前宽高是否有效,有效则调用onSizeReady,无效则委托target去获取宽高
-
3.回调target的onLoadStarted方法,ImageViewTarget对象的该方法会去给ImageView设置占位符 我们是第一次请求,所以会走步骤2,我们采用的都是默认配置,overrideWidth和overrideHeight都是默认值-1,所以这里isValidDimensions会返回fasle,这样会调用target的getSize去获取视图宽高
public void getSize(@NonNull SizeReadyCallback cb) { sizeDeterminer.getSize(cb); } void getSize(@NonNull SizeReadyCallback cb) { int currentWidth = getTargetWidth();//通过View的layoutParam去获取View宽高 int currentHeight = getTargetHeight(); if (isViewStateAndSizeValid(currentWidth, currentHeight)) { //回调出去,这里的cb就是request对象 cb.onSizeReady(currentWidth, currentHeight); return; } if (!cbs.contains(cb)) { cbs.add(cb); } if (layoutListener == null) { //通过ViewTreeObserver去观测view,在绘制之前会再次尝试从View中获取 ViewTreeObserver observer = view.getViewTreeObserver(); layoutListener = new SizeDeterminerLayoutListener(this); observer.addOnPreDrawListener(layoutListener); } } 所以getSize获取到视图宽高时,还是会回调到request的onSizeReady方法
public void onSizeReady(int width, int height) { ...//省略异常状态处理 status = Status.RUNNING; 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); } onSizeReady方法会将request的状态从WAITING_FOR_SIZE切换到RUNNING,并委托engine去load资源
public LoadStatus load( GlideContext glideContext, Object model, //数据model,这里是之前传递的url Key signature, //空key int width, //view的宽度 int height,//view的高度 Class resourceClass, //默认是Object.class Class transcodeClass, //Drawable的Class Priority priority, DiskCacheStrategy diskCacheStrategy, //默认是DiskCacheStrategy.AUTOMATIC Map, Transformation<?>> transformations, //转换器集合 boolean isTransformationRequired, //默认是false boolean isScaleOnlyOrNoTransform, //默认是false Options options, boolean isMemoryCacheable, //默认是true boolean useUnlimitedSourceExecutorPool, //false boolean useAnimationPool, //false boolean onlyRetrieveFromCache,//false,表示是否仅从缓存中恢复数据 ResourceCallback cb // request对象) {
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); return null; }
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); return null; }
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null) { current.addCallback(cb); return new LoadStatus(cb, current); }
EngineJob engineJob = engineJobFactory.build( key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache);
DecodeJob 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); engineJob.start(decodeJob); return new LoadStatus(cb, engineJob); }
engine的load方法分为3步来获取资源
-
1.通过loadFromActiveResources方法获取激活的资源
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) { if (!isMemoryCacheable) { return null; } EngineResource<?> active = activeResources.get(key); if (active != null) { active.acquire(); } return active; }- 尝试从activeResources中获取资源,这个activeResources采用Map+弱引用的方式保存着当前正在被使用的资源,这里如果获取到了资源,则通过acquire方法给该资源计数(该资源使用者数量++)
- 如果从loadFromActiveResources方法获取到了资源,则回调onResourceReady方法给request对象,后者会委托Viewtarget设置该资源给View展示
-
2.通过loadFromCache方法获取缓存的资源
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) { EngineResource<?> cached = getEngineResourceFromCache(key); if (cached != null) { cached.acquire(); activeResources.activate(key, cached); } return cached; } private EngineResource<?> getEngineResourceFromCache(Key key) { Resource<?> cached = cache.remove(key); final EngineResource<?> result; if (cached == null) { result = null; } else if (cached instanceof EngineResource) { // Save an object allocation if we've cached an EngineResource (the typical case). result = (EngineResource<?>) cached; } else { result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/); } return result; }- loadFromCache尝试从cache中获取资源,如果获取到,则将该资源添加到activeResources中并从cache中移除。这里的cache就是创建Glide时的MemoryCache。这里activeResource和cache的区别就是activeResource保存资源正在被使用,而cache保存的资源当前没有被使用。但是从这两个中获取的资源都算是从缓存中获取。所以DataSource类型为DataSource.MEMORY_CACHE。
-
3.从引擎任务中获取任务,如果没有任务,则新建任务并开始任务
public void start(DecodeJob<R> decodeJob) { this.decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); } boolean willDecodeFromCache() { Stage firstStage = getNextStage(Stage.INITIALIZE); return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE; } private Stage getNextStage(Stage current) { switch (current) { case INITIALIZE: return diskCacheStrategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE); case RESOURCE_CACHE: return diskCacheStrategy.decodeCachedData() ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE); case DATA_CACHE: return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE; case SOURCE: case FINISHED: return Stage.FINISHED; default: throw new IllegalArgumentException("Unrecognized stage: " + current); } }
engineJob的start方法先根据decodeJob的willDecodeFromCache方法获得业务方设定的是否允许从缓存中读取数据的配置来决定选择哪一个线程池执行器,willDecodeFromCache方法主要是根据不同的Stage状态来调用磁盘缓存策略(diskCacheStrategy)的相关api获取结果,我们没有设置自定义的缓存策略,所以这里默认是DiskCacheStrategy.AUTOMATIC。这里我们可以发现对应engine,其执行方式是按照不同阶段来执行,对应与不同的Stage,对应关系如下
| Stage | engineJob执行阶段 |
|---|---|
| INITIALIZE | 默认状态,引擎任务初始化阶段 |
| RESOURCE_CACHE | 从缓存文件中加载那些经过转换或者降低了采样率的资源数据 |
| DATA_CACHE | 从缓存文件中加载原始的未修改的资源阶段 |
| SOURCE | 加载原始资源阶段,这里会根据磁盘缓存策略可能会先写入本地,再从本地读取,而不是直接返回资源 |
| ENCODE | 编码阶段,比如将内存中的bitmap写入本地缓存 |
| FINISHED | 没有后续可执行的阶段了,可以看成是引擎任务结束了 |
| 上述engineJob执行阶段会根据缓存策略来选择执行的阶段,该缓存策略就是我们日常使用的DiskCacheStrategy,该类定义了4个方法指示缓存粗略,而glide内部默认实现了5种缓存策略。 | |
| 方法 | 含义 |
| isDataCacheable(DataSource dataSource) | 是否需要缓存原始且未修改过的数据 |
| isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) | 是否需要缓存经过转换的资源 |
| decodeCachedResource() | 是否允许从缓存的资源只能够解码,即是否允许使用本地缓存和经过修改的资源 |
| decodeCachedData() | 是否允许从本地读取原始资源 |
| 这里我们采用的是DiskCacheStrategy.AUTOMATIC,所以看下ta是怎么实现的 |
public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
@Override
public boolean isDataCacheable(DataSource dataSource) {
//如果该资源来自远程,就缓存
return dataSource == DataSource.REMOTE;
}
@Override
public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
EncodeStrategy encodeStrategy) {
return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
|| dataSource == DataSource.LOCAL)
&& encodeStrategy == EncodeStrategy.TRANSFORMED;
}
@Override
public boolean decodeCachedResource() {
//允许从本地缓存读取那些可能经过修改的资源
return true;
}
@Override
public boolean decodeCachedData() {
//允许从缓存中读取原始资源
return true;
}
};
上述实现中多次用到DataSource这个类,其主要作用是标记数据起源,对应关系如下:
| DataSource | 起源解释 |
|---|---|
| LOCAL | 表明该数据可能来自本地设备 |
| REMOTE | 表明该数据来自远程,比如服务器 |
| DATA_DISK_CACHE | 表明该数据来自本地磁盘缓存,原始未修改的数据 |
| RESOURCE_DISK_CACHE | 表明该数据来自本地磁盘缓存,且该数据相对原始数据有修改 |
| MEMORY_CACHE | 表明该数据来自内存 |
回到getNextStage方法,这里初始状态为INITIALIZE,而diskCacheStrategy.decodeCachedResource()返回true,所以下一个stage为Stage.RESOURCE_CACHE,所以willDecodeFromCache方法将会返回true,即使用磁盘线程池执行器,接着调用委托线程池去执行decode任务,也就是会调用到decode的run方法:
public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
...//是否已取消
runWrapped();
} catch (Throwable t) {
...//
run方法主要关注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);
}
}
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())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
}
之前我们说了,decode执行的策略是按照stage,分阶段来执行,默认stage是INITIALIZE,所以runWrapped会走INITIALIZE的case, 并且getNextStage会返回RESOURCE_CACHE,然后会根据该Stage选出对应的数据拉取工作对象,即ResourceCacheGenerator,之后会在runGenerators调用数据拉取对象的startNext方法。
public boolean startNext() {
...//获取modelLoader
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData = modelLoader.buildLoadData(cacheFile,
helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
startNext方法主要分为2步
- 根据model找到对应的modelLoader,model就是我们load(url)方法传递的String类型的URL,而modelLoader为Glide设计用来加载和转换数据的,比如给定Url,获取到对应Url的InputStream。modelLoader的内部实现实际上委托给了对应的dataFetcher。这里的modelLoader实际就对应了具体需要从那里加载数据,如果在这里获取的modelLoader是从本地磁盘的类型(File相关),那么dataFatcher就会从本地加载。我们这里是没有本地缓存所以实际上这里会返回false,
- 如果第一步中找到了具体的modelLoader,就委托相关fetcher去加载数据。
由于我们没有本地缓存,所以回到runGenerators方法,此时stage为RESOURCE_CACHE,获取下一个stage时diskCacheStrategy.decodeCachedData()返回fasle,所以下一个stage为Stage.DATA_CACHE,从而下一个DataFetcherGenerator为DataCacheGenerator,该对象本质上也是从本地缓存加载资源,和ResourceCacheGenerator的不同就是,ResourceCacheGenerator加载有转换和修改过的本地资源,DataCacheGenerator加载未修改过的原始资源,同样的,这里也会返回false。 再次回到runGenerators方法。此时stage为Stage.DATA_CACHE,获取下一个Stage为Stage.SOURCE,获取对应的DataFetcherGenerator类型为SourceGenerator,看下该对象的startNext方法
public boolean startNext() {
//dataToCache为null,不为null时用于处理缓存资源到本地
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;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
SourceGenerator的startNext方法,前面主要检测是否需要缓存当前资源到本地,此时没有需要缓存到资源。接下里会获取对应的LoadData,然后使用对应LoadData的dataFetcher去加载数据,这里看下helper的getLoadData方法返回了什么类型的LoadData;
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
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;
}
这里从glideContex中获取modelLoader,此时model是我们之前传递的图片的Strig类型的Url,按照类型会找到StringLoader对象,而StringLoader对象只是一个空壳,内部委托给了一个复合型的MultiModelLoader,该loader对象内部使用集合管理了多个ModelLoader,按照在集合中的顺序,从0开始遍历去找到loader去处理请求,这里会找到OkHttpStreamFetcher去获取资源
public void loadData(@NonNull Priority priority,
@NonNull final DataCallback<? super InputStream> callback) {
Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());
for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();
this.callback = callback;
call = client.newCall(request);
call.enqueue(this);
}
public void onResponse(@NonNull Call call, @NonNull Response response) {
responseBody = response.body();
if (response.isSuccessful()) {
long contentLength = Preconditions.checkNotNull(responseBody).contentLength();
stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
callback.onDataReady(stream);
} else {
callback.onLoadFailed(new HttpException(response.message(), response.code()));
}
}
可以看到OkHttpStreamFetcher的loadData方法直接使用OkhttpClient的newCall创建了一个请求,在onRespone中接受响应数据,并回调callback.onDataReady(stream);将inputStream回调出去。这个callback就是SourceGenerator对象,所以看下SourceGenerator对象的onDataReady方法
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;//!!!!这里设置了dataToCache
cb.reschedule();
} else {
//直接通知DecodeJob,当前原始数据已经获取到了
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
该方法中data是InputStream类型,diskCacheStrategy为默认的DiskCacheStrategy.AUTOMATIC,而DataSource为DataSource.REMOTE,所以会走到cb.reschedule();这里的cb为DecodeJob对象。而DecodeJob的reschedule方法又会调用其callback的reschedule方法,最终回调到engineJob的reschedule方法:
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
##engineJob.java
public void reschedule(DecodeJob<?> job) {
getActiveSourceExecutor().execute(job);
}
其实reschedule回调就是让decodeJob在执行一次,只不过此时reason为SWITCH_TO_SOURCE_SERVICE:
private void runWrapped() {
switch (runReason) {
...//
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
...//
}
}
再次执行runGenerators方法,此时Stage为Stage.SOURCE,currentGenerator为SourceGenerator对象,绕来绕去又回到了SourceGenerator对西那个的startNext方法,
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
...//
}
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());
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);
}
此时dataToCache不为Null,调用到cacheData方法,该方法获取对应到Encoder将原始数据写入本地磁盘,再通知dataFetcher清理数据。然后新建一个DataCacheGenerator对象赋值给sourceCacheGenerator,这样startNext方法就会调用该sourceCacheGenerator对象的startNext方法,该方法会尝试从磁盘获取资源,并最终一路向上回调到DecodeJob对象的onDataFetcherReady方法,
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;// DataSource.LOCAL
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
其最终回调到decodeFromRetrievedData方法:
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
decodeFromRetrievedData方法做了两件事:
-
从本地数据流中解码资源,这里输入数据类型为InputStream,而输出类型为Bitmap,所以获取到StreamBitmapDecoder类型的解码器。该解码其会从inputStream中解码出bitmap
-
通知外部资源已准备好了,这里会调用到engineJob的onResourceReady方法
public void onResourceReady(Resource<R> resource, DataSource dataSource) { this.resource = resource; this.dataSource = dataSource; MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget(); } public boolean handleMessage(Message message) { EngineJob<?> job = (EngineJob<?>) message.obj; switch (message.what) { case MSG_COMPLETE: job.handleResultOnMainThread(); break; } return true; } void handleResultOnMainThread() { ...// engineResource = engineResourceFactory.build(resource, isCacheable); hasResource = true; // Hold on to resource for duration of request so we don't recycle it in the middle of // notifying if it synchronously released by one of the callbacks. 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(); cb.onResourceReady(engineResource, dataSource); } } // Our request is complete, so we can release the resource. engineResource.release(); release(false /*isRemovedFromQueue*/); }
handleResultOnMainThread方法首先创建了EngineResource对象并计数,来包装代表资源的Resource,接着向上通知当前job已经完成,最后释放了engine资源。通知包含两步:
-
listener.onEngineJobComplete,通知Engine
-
cb.onResourceReady通知SingleRuqest。
##Engine public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) { if (resource != null) { resource.setResourceListener(key, this); if (resource.isCacheable()) { //如果资源允许缓存,则将其添加到activeResources中 activeResources.activate(key, resource); } } jobs.removeIfCurrent(key, engineJob); } public void onResourceReady(Resource<?> resource, DataSource dataSource) { Object received = resource.get(); //...做一些状态判断 onResourceReady((Resource<R>) resource, (R) received, dataSource); } private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) { boolean isFirstResource = isFirstReadyResource(); status = Status.COMPLETE; this.resource = resource; try { if (!anyListenerHandledUpdatingTarget) { Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource); target.onResourceReady(result, animation);!!!通知ViewTarget该资源已经准备好了 } } notifyLoadSuccess(); }