问题
上文已经简单解释了一个Request的生成过程,本文目标如下
- Glide在load中传入的数据最后是如何转成显示的Drawable的
- 传入的数据如何在ImageView上显示
注意由于代码分支过多,会删减一些源码便于理解
流程解析
Request.begin
书接上文,SingleRequest的begin被调用之后
//SingleRequest.java
public void begin() {
//这里的方法按照注释来说是如在我们调用了notifyDataSetChanged之后,即开启了同一个request,就可以直接使用之前所加载过的resource
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
//我们所设置的width和hegith在合法范围内,则表示size已经准备就绪
onSizeReady(overrideWidth, overrideHeight);
} else {
//如果不合法,这里会通过ViewTarget去拿如ImageView的size
target.getSize(this);
}
//在执行完上面的onSizeReady后正常就是RUNNING状态,此时会在这里告诉ViewTarget去直接使用我们设置的加载中的Drawable
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
}
public void onSizeReady(int width, int height) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
//把当前的status状态由WAITING_FOR_SIZE改为RUNNING
status = Status.RUNNING;
//如果设置了sizeMultiplier,则会按照我们的要求对宽高进行缩放
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
//engine在Glide实例初始化时创建,其load方法
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,
callbackExecutor);
}
上面的SingleRequest代码总结一下就是
- 一个Request启动后,会先计算得到它最终所呈现的宽高,后调用Engine的load方法继续加载
- 设置的placeholder加载中图片会在这一步呈现到界面上
//Engine.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) {
//通过传入的model、宽高、Drawable.class、参数等合并成一个key
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
//这里是从内存缓存里去找是否已经存了Resource,我们在本问之后会说这里
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);
}
}
//若从缓存中拿到了,直接回调onResourceReady,这里的cb是SingleRequest
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
上面的代码看着就很恐怖的感觉,但是大部分都是参数列表,其实就是先从缓存里找有没有现成的资源,没有就继续调用waitForExistingOrStartNewJob。
我们先不管缓存的部分,看看没缓存的情况下是怎么处理的
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) {
//jobs可以理解成存储EngineJob的列表,先看有没有现成的同样请求的EngineJob
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
//第一次请求肯定是没有的,我们就不管这个if内部了
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
//创建了一个EngineJob<Drawable>
//这个对象里含有一堆线程池,主要用来给各个任务提供线程池去加载,并统一由它接受资源返回,并回调出来
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
//创建了一个DecodeJob<Drawable>
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
//通过EngineJob里的线程池启动一个decodeJob
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
//EngineJob.java
public synchronized void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
//如果我们的任务需要从硬盘中拉取数据,则使用diskCacheExecutor线程池
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
//获取应该使用哪个线程池进行调用
private GlideExecutor getActiveSourceExecutor() {
return useUnlimitedSourceGeneratorPool
? sourceUnlimitedExecutor
: (useAnimationPool ? animationExecutor : sourceExecutor);
}
上述代码其实就是通过EngineJob启动DecodeJob,EngineJob中含有的几个线程池简单介绍一下
- animationExecutor gif加载帧使用的线程池,在GifFrameLoader(Glide的gif相关加载类)中有设置useAnimationPool为true的情况,我们在使用时也可以通过类似方法进行指定,其线程数与cpu相关
- sourceUnlimitedExecutor Glide源码内没有使用的地方,是一个无限容量的线程池,且没有核心线程,网上理解是用于同时加载超大量图片的情况
- diskCacheExecutor 硬盘相关缓存操作的线程池,只有一个线程,保证文件串行操作
- sourceExecutor 取可用cpu数为核心线程,最大值为4,主要是这个线程池用于进行DecodeJob的任务
这里稍微提一下Glide中是如何指定使用animationExecutor加载DecodeJob的
//GifFrameLoader.java
生成一个gif帧的加载请求
private static RequestBuilder<Bitmap> getRequestBuilder(
RequestManager requestManager, int width, int height) {
return requestManager
.asBitmap()
.apply(
diskCacheStrategyOf(DiskCacheStrategy.NONE)
.useAnimationPool(true)
.skipMemoryCache(true)
.override(width, height));
}
既然是通过EncodeJob里的线程池来启动DecodeJob,那么DecodeJob其实就是一个Runnable,注意从下面的方法开始,就不再是在我们调用的ui线程执行了
我们来看一下它的run方法
//DecodeJob.java
public void run() {
//省略其它代码
runWrapped();
}
private void runWrapped() {
//runReason默认是INITIALIZE状态
switch (runReason) {
case INITIALIZE:
//当前的步骤从INITIALIZE跳到下一步
stage = getNextStage(Stage.INITIALIZE);
//根据当前步骤获取对应的Generator,可以理解为接下来要执行的任务的封装
currentGenerator = getNextGenerator();
//执行currentGenerator的startNext方法
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
//根据当前状态获取下一步
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);
}
}
获取下一步的代码比较容易理解,下面总结一下
- 我们设置了可以缓存最终数据Resource,则通过ResourceCacheGenerator获取
- 有设置缓存源数据DATA,则通过DataCacheGenerator获取
- 没有缓存,通过SourceGenerator获取
我们当前是无缓存的,所以currentGenerator为SourceGenerator,执行它的startNext方法
//SourceGenerator.java
@Override
public boolean startNext() {
//dataToCache默认没有值,只在数据解码完成后由onDataReady方法赋值
//所以这个条件的代码时和缓存相关的,我们暂时不去管,之后的文章说缓存时再去考虑
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
//这里的sourceCacheGenerator与上面一样也是与缓存相关,暂时不用管
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
//这里的意思是遍历List<LoadData>,用每个loadData对象来尝试解析传入的model数据
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的执行过程:遍历List,通过loadData对象去解析model数据。而loadData这个对象就是解析model数据的关键
LoadData的获取
我们先看一下LoadData如何获取的,它通过helper.getLoadData得到列表,而helper则是在DecodeJob初始化时实例化的decodeHelper对象,且每个DecodeJob里都带有一个decodeHelper对象。
//DecodeHelper.java
//此方法的作用
//model->获取List<ModelLoader>->获取List<LoaderData>
List<LoadData<?>> getLoadData() {
//每个DecodeHelper只会初始化一次
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
//注意这里,glideContext对于Glide就类似ApplicationContext对于Application一样的存在
//所以这里就是到整个Glide共享的Registry中通过model(我们传入的数据)获取可用的modelLoader列表
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
//通过modelLoader去获取LoadData
LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
上面的getModelLoaders(model)就不把方法贴出来了,内容其实比较单一,就是通过model的类型去找对应的ModelLoader,且限定条件为ModelLoader的handles方法返回true才可以。
而ModelLoader的工厂方法列表都在Glide初始化时会创建,下面贴出一小部分
//Glide.java
//这是Glide的构造函数
Glide(
@NonNull Context context,
@NonNull Engine engine,
//..省略一大堆参数
int minHardwareDimension) {
registry
//输入为File,输出为InputStream的modelLoader
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
//输入为resId,输出同上
.append(int.class, InputStream.class, resourceLoaderStreamFactory)
//输入为url,输出同上
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
//输入为File,输出为ByteBuffer
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
//输入为String,输出为InputStream
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
//...省略一大堆append
}
从上面可推,当我们输入为一个string类型的网址时,得到就是所有输入为String的modelLoader;当我们输入为资源id时,得到的就是所有输入为int的modelLoader。这就是modelLoader的来源
回到本节的开始,获取了List后会调用每个ModelLoader的buildLoadData方法
List<LoadData<?>> getLoadData() {
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
//调用每个modelLoader的buildLoadData
LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
}
}
这里我们以传入了一个String类型的图片网络地址为例,入参为String的modelLoader在glide初始化时加入的有
- StringLoader
- DataUrlLoader
而他们的handles方法分别返回了
//DataUrlLoader.java
public boolean handles(@NonNull Model model) {
//它只处理String开头为data:image的字符串
return model.toString().startsWith("data:image");
}
//StringLoader.java
@Override
public boolean handles(@NonNull String model) {
//我都处理
return true;
}
所以对于我们的实例情况,最后List只有StringLoader一个符合要求,但是打断点确认这里其实是有三个StringLoader对象的,到Glide初始化的地方确认一下刚刚好是三个,输出分别是InputStream、ParcelFileDescriptor、AssetFileDescriptor
.append(String.class, InputStream.class, new StringLoader.StreamFactory())
.append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
.append(
String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
//StringLoader.java
public LoadData<Data> buildLoadData(
@NonNull String model, int width, int height, @NonNull Options options) {
//把model转换成uri对象
Uri uri = parseUri(model);
if (uri == null || !uriLoader.handles(uri)) {
return null;
}
//调用uriLoader的buildLoadData方法,上面是三个StringLoader的uriLoader都不同
return uriLoader.buildLoadData(uri, width, height, options);
}
对于uriLoader来说,其实是与StringLoader同级的,这里相当于StringLoader先把String转化为uri,后续再转接到其它输入为Uri的ModelLoader来处理,这里不再赘述,看下图即可,从左往右为输入,从右往左为输出
graph LR
StringLoader-->HttpUrlLoader
HttpUrlLoader-->HttpGlideUrlLoader
HttpGlideUrlLoader-->HttpUrlLoader
HttpUrlLoader-->StringLoader
所以最后其实是调用到HttpGlideUrlLoader中进行model处理
//HttpGlideUrlLoader.java
public LoadData<InputStream> buildLoadData(
@NonNull GlideUrl model, int width, int height, @NonNull Options options) {
GlideUrl url = model;
//LoadData传入了model与HttpUrlFetcher对象
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
对于LoadData而言,它只是一个存储类而已,存储这个ModelLoader的数据封装工具(对于实例来说就是HttpUrlFetcher)
ModelLoader的执行
回到SourceGenerator中调用LoadData的位置
//SourceGenerator.java
public boolean startNext() {
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;
//慈湖会调用fetcher的loadData方法
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
这里会调用到HttpUrlFetcher中
//HttpUrlFetcher.java
public void loadData(
@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
//通过loadData拿到最终的生成类InputStream
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
//回调到SourceGenerator的onDataReady中
callback.onDataReady(result);
}
private InputStream loadDataWithRedirects(
URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
//http的重定向相关,最多只支持5次重定向
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
//如果上一次重定向和这一次的url相同,那么就是死循环了
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.
}
}
//眼角有泪流出,终于看到了熟悉的方法HttpUrlConnection
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);
urlConnection.setInstanceFollowRedirects(false);
//开启连接
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)) {
//通过connection获取InputStream,和我们使用httpUrlConnection的方式相同,这里就不做解释了
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);
}
}
由以上内容得知
- 我们的网络图片地址其实是通过SourceGenerator在HttpUrlConnect转换成InputStream的,这个InputStream就相当于我们数据的中间类
- 我们load进入的数据都可以通过查看输入类型找到对应的ModelLoader,从而来确认Glide内部是如何实现的(如传入的resId则通过ResourceLoader并转给UriLoader.StreamFactory来处理)
- 最终要展示到ImageView上肯定还是得通过Drawable来实现的,我们还需要找到InputStream如何转化为可用的Drawable
graph LR
Model-->InputStream
InputStream-->Drawable
InputStream后续如何处理
InputStream获取成功后,会回调到SourceGenerator的onDataReady方法
//SourceGenerator.java
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
//硬盘缓存相关
dataToCache = data;
//reschedule逻辑比较乱,但是捋顺了会发现作用其实只是把当前要做的任务通过EncodeJob切换到正确的线程上去
//而这里需要做的任务就是进行硬盘缓存,缓存相关逻辑会专门写一篇文章处理
cb.reschedule();
} else {
//我们先不管上面的逻辑,主要跟踪InputStream
//这里的cb是DecodeJob
cb.onDataFetcherReady(
loadData.sourceKey,
data,
loadData.fetcher,
loadData.fetcher.getDataSource(),
originalKey);
}
}
接下来就是正式开始解码数据,即把InputStream转化为我们需要的内容(Drawable)
//DecodeJob.java
@Override
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
if (Thread.currentThread() != currentThread) {
//如上面的代码块所说,切换到正确的decode线程去
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
private void decodeFromRetrievedData() {
Resource<R> resource = null;
//这里的resource不是我们自己的resource,只是Glide对最终生成类的封装
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
if (resource != null) {
//获得到了resource,我们的Drawable也包含在里面,这样就可以一直往外回调给ImageView设置Target了
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private <Data> Resource<R> decodeFromData(
DataFetcher<?> fetcher, Data data, DataSource dataSource) throws GlideException {
Resource<R> result = decodeFromFetcher(data, dataSource);
return result;
}
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
//又是这个decodeHelper...之前的LoadData也是从他们这里获取的,不过传入的key变成了我们最后要生成的Drawable.class,那么这里的LoadPath作用也会与LoadData类似
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
//LoadPath的执行
return runLoadPath(data, dataSource, path);
}
又要到decodeHelper中去处理类型转换相关的内容了,注意这里用断点的可以很方便的理解获取LoadPath的过程
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
@NonNull Class<Data> dataClass,
@NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
//先从缓存里找,我们没有缓存就不管
LoadPath<Data, TResource, Transcode> result =
loadPathCache.get(dataClass, resourceClass, transcodeClass);
if (loadPathCache.isEmptyLoadPath(result)) {
//这里并不是判空,只是对特殊的LoadPath进行验证,不用管
return null;
} else if (result == null) {
//与之前的List<LoadData>一样,这里获取的是List<DecodePath>
List<DecodePath<Data, TResource, Transcode>> decodePaths =
getDecodePaths(dataClass, resourceClass, transcodeClass);
if (decodePaths.isEmpty()) {
result = null;
} else {
result =
new LoadPath<>(
dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}
关键的DecodePath的生成,直接生看源码可能会比较乱,所以这里我们举一个加载网络地址图片到ImageView的例子(不管缓存)
//使用例
Glide.with(this)
.load("https://xxxxxx.jpg")
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(iv_image)
//按照例子,输入的对象分别为
//dataClass=InputStream.class
//resourceClass=Object.class
//transcodeClass=Drawable.class
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
@NonNull Class<Data> dataClass,
@NonNull Class<TResource> resourceClass,
@NonNull Class<Transcode> transcodeClass) {
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
List<Class<TResource>> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass);
for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses =
transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
//通过decoderRegistry获取Decoders列表
List<ResourceDecoder<Data, TResource>> decoders =
decoderRegistry.getDecoders(dataClass, registeredResourceClass);
//通过transcodeRegistry获取Transcoder列表
ResourceTranscoder<TResource, Transcode> transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
//通过获取的数据组装成一个DecodePath对象
DecodePath<Data, TResource, Transcode> path =
new DecodePath<>(
dataClass,
registeredResourceClass,
registeredTranscodeClass,
decoders,
transcoder,
throwableListPool);
decodePaths.add(path);
}
}
return decodePaths;
}
上面看起来一大段,其实也是和获取ModelLoader一样,通过输入类型与输出类型找到所需的Decoder,所以我们还是看看Glide初始化有添加那些Decorder
//
.append(
//第一个参数是给Decorder分类用的,比如BUCKERT_BITMAP_DRAWABLE就是统一生成BitmapDrawable对象的Decorder
Registry.BUCKET_BITMAP_DRAWABLE,
//输入
ByteBuffer.class,
//输出
BitmapDrawable.class,
//正宗Decorder对象
new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
InputStream.class,
BitmapDrawable.class,
//这个输入与输出都符合我们的要求
new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
.append(
Registry.BUCKET_BITMAP_DRAWABLE,
ParcelFileDescriptor.class,
BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, parcelFileDescriptorVideoDecoder))
/* GIFs */
.append(
Registry.BUCKET_GIF,
InputStream.class,
GifDrawable.class,
new StreamGifDecoder(imageHeaderParsers, byteBufferGifDecoder, arrayPool))
.append(Registry.BUCKET_GIF, ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
综上可得
- 其实InputStream接下来会被Decorder转化成对应的Drawable对象
- LoadPath其实和LoadData一样,保存List用于后续遍历尝试解码
Decorder的处理
Decorder会存在LoadData内,由DecodeJob在解析出InputStream后遍历尝试解码
//Decorder.java
private <Data, ResourceType> Resource<R> runLoadPath(
Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path)
throws GlideException {
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
}
private Resource<Transcode> loadWithExceptionList(
DataRewinder<Data> rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions)
throws GlideException {
Resource<Transcode> result = null;
//遍历LoadPath中的DecodePath列表,调用其decode函数处理
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
//DecodePath.java
public Resource<Transcode> decode(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
DecodeCallback<ResourceType> callback)
throws GlideException {
//解码的关键方法decodeResource,按照实例的返回是一个Bitmap
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
//解码完成之后回调到DecodeJob的onResourceDecoded
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
//实例会把Bitmap转码成BitmapDrawable
return transcoder.transcode(transformed, options);
}
在往里跟方法前先给一个通过断点调试得出的结论
- decodeResource会把InputStream转化成Bitmap
- trascode方法会把Bitmap转化成BitmapDrawable
所以这里我们分成两步
InputStream–>Bitmap
private Resource<ResourceType> decodeResource(
DataRewinder<DataType> rewinder, int width, int height, @NonNull Options options)
throws GlideException {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
}
private Resource<ResourceType> decodeResourceWithList(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
List<Throwable> exceptions)
throws GlideException {
Resource<ResourceType> result = null;
for (int i = 0, size = decoders.size(); i < size; i++) {
//遍历Decorder
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
DataType data = rewinder.rewindAndGet();
//和ModelLoader一样,通过handles判断是否能够处理这个类型的数据
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
//可以就去解码
result = decoder.decode(data, width, height, options);
}
//解码成功之后,直接返回,不用再继续遍历了
if (result != null) {
break;
}
}
//如果result还是null,那么Decorder将会全军覆没,哈哈哈哈哈
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
那么我们其实就可以根据输入为InputStream的Decorder找出对应的Decorder就是我StreamBitmapDecoder哒!从而也可以得知StreamBitmapDecoder的输出就是Bitmap.class
//StreamBitmapDecorder.java
public Resource<Bitmap> decode(
@NonNull InputStream source, int width, int height, @NonNull Options options)
throws IOException {
//这里是对InputStream的一层封装
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
//这里也是对InputStream的一层封装
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
//最后其实是通过这个解码的
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
下面就是真正解码成Bitmap的位置!
//Downsampler.java
public Resource<Bitmap> decode(
InputStream is,
int requestedWidth,
int requestedHeight,
Options options,
DecodeCallbacks callbacks)
throws IOException {
byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
//熟悉的类BitmapFactory!!!
BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
bitmapFactoryOptions.inTempStorage = bytesForOptions;
//默认解码发现在新版本已经变成了PREFER_ARGB_8888
DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
PreferredColorSpace preferredColorSpace = options.get(PREFERRED_COLOR_SPACE);
DownsampleStrategy downsampleStrategy = options.get(DownsampleStrategy.OPTION);
boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
boolean isHardwareConfigAllowed =
options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG);
try {
Bitmap result =
decodeFromWrappedStreams(
is,
bitmapFactoryOptions,
downsampleStrategy,
decodeFormat,
preferredColorSpace,
isHardwareConfigAllowed,
requestedWidth,
requestedHeight,
fixBitmapToRequestedDimensions,
callbacks);
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions);
}
}
private Bitmap decodeFromWrappedStreams(
InputStream is,
BitmapFactory.Options options,
//省略一堆入参
DecodeCallbacks callbacks)
throws IOException {
//这里是先得到源数据的宽高,用的是inJustDecodeBounds的方式
int[] sourceDimensions = getDimensions(is, options, callbacks, bitmapPool);
int sourceWidth = sourceDimensions[0];
int sourceHeight = sourceDimensions[1];
//获得目标宽高
int targetWidth =
requestedWidth == Target.SIZE_ORIGINAL
? (isRotationRequired(degreesToRotate) ? sourceHeight : sourceWidth)
: requestedWidth;
int targetHeight =
requestedHeight == Target.SIZE_ORIGINAL
? (isRotationRequired(degreesToRotate) ? sourceWidth : sourceHeight)
: requestedHeight;
//解码出图片类型,如jpg、png、gif等
ImageType imageType = ImageHeaderParserUtils.getType(parsers, is, byteArrayPool);
//...
//从bitmapPool中得到可复用的Bitmap,并存入options.inBitmap
if (expectedWidth > 0 && expectedHeight > 0) {
setInBitmap(options, bitmapPool, expectedWidth, expectedHeight);
}
//注意注意注意啦,关键的解码出Bitmap的地方,里面其实就是调用了BitmapFactory.decodeStream
Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
//...
return rotated;
}
所以InputStream其实还是通过BitmapFactory.decodeStream解码出Bitmap的,并且也用到了inJustDecodeBounds、inBitmap等优化方式
Bitmap–>BitmapDrawable
接下来decode结束,到了Transcoder转码器发挥的时候了,还是一样,我们查看Glide初始化时注册的Transcoder,并且也可以找到对应的输入和输出
//Glide.java
/* Transcoders */
.register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))
.register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
.register(
Drawable.class,
byte[].class,
new DrawableBytesTranscoder(
bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
.register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
输入为Bitmap,输出为Drawable,选哪个不用我多说了吧
//BitmapDrawableTranscoder.java
public Resource<BitmapDrawable> transcode(
@NonNull Resource<Bitmap> toTranscode, @NonNull Options options) {
//这里最终其实就是生成了一个LazyBitmapDrawableResource,就是一个BitmapDrawable的生成类
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
//LazyBitmapDrawableResource.java
public BitmapDrawable get() {
//它的get方法就可以获取到BitmapDrawable了
return new BitmapDrawable(resources, bitmapResource.get());
}
展示到ImageView
在此次DecodeJob任务完成之后,会经过如下的调用链
0. DecodeJob#notifyEncodeAndRelease
- EncodeJob#onResourceReady
- CallResourceReady#run
- SingleRequest#onResourceReady
- ImageViewTarget#onResourceReady
- BitmapImageViewTarget#setResource
- view.setImageBitmap(resource)
经过一长串的调用之后终于写到了ImageView上,Glide数据加载部分就此结束
总结
- SingeRequest启动一个任务后,先确认最终所需加载的宽高
- 通过EngineJob里的线程池启动一个DecodeJob
- DecodeJob会获取一个ModelLoader,可以把一个url加载成InputStream
- 通过Decoder把InputStream加载成Bitmap
- 通过Transcoder把Bitmap转化成BitmapDrawable
- 最终通过setImageBitmap展示到ImageView上