介绍
从上一篇文章我们知道了如何从Model得到Data。拿到Data数据后还要经过解码和转码才能得到我们app上需要的资源,这篇文章分析下解码,转换 ,转码的工作流程。
先说明一下这几个过程的作用:
- 解码过程就是通过Data(InputStream等)得到Resource(bitmap等)。
- 转换过程就是将Bitmap经过ImageView 的ScaleTyp转为特定的样式。
- 转码过程就是把BitmapResource转换为高层模块需要的DrawableResource。
准备工作
首先需要根据传入的Data(InputStream) Resource(Object) 和transcodeResource(Drawable)找到后边解码和转码需要的资源和解码器和转码器
DecodeJob.java
@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;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
/**从接收到的数据解码*/
private void decodeFromRetrievedData() {
Resource resource = null;
...
resource = decodeFromData(currentFetcher, currentData, currentDataSource);//1解码
...
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);//1 通知编码和释放资源
} else {
runGenerators();
}
}
/**使用DataFetcher 根据Data解码资源*/
private Resource decodeFromData(
DataFetcher> fetcher, Data data, DataSource dataSource) throws GlideException {
try {
...
Resource result = decodeFromFetcher(data, dataSource);
...
return result;
} finally {
fetcher.cleanup();
}
}
private Resource decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath path = decodeHelper.getLoadPath((Class) data.getClass());//3查找LoadPath
return runLoadPath(data, dataSource, path);
}
private Resource runLoadPath(
Data data, DataSource dataSource, LoadPath path)
throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder rewinder = glideContext.getRegistry().getRewinder(data);//1
....
return path.load(
rewinder, options, width, height, new DecodeCallback(dataSource));//2
....
}
我们看一下注释3处的查找LoadPath,这个在前边的SourceGenerator中startNext()方法内helper.hasLoadPath(loadData.fetcher.getDataClass()))的一样的,所以放到这里分析。DecodeHelper.java
LoadPath getLoadPath(Class dataClass) {//Inputstream.class
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
根据dataClass, resourceClass, transcodeClass从注册表中查找所有需要的资源
Registry#getLoadPath
@Nullable
public LoadPath getLoadPath(
@NonNull Class dataClass,//InputStream.class
@NonNull Class resourceClass,//Object.class
@NonNull Class transcodeClass) {//Drawable.class
LoadPath result =
loadPathCache.get(dataClass, resourceClass, transcodeClass);
...
List> decodePaths =
getDecodePaths(dataClass, resourceClass, transcodeClass);
...
result =
new LoadPath<>(
dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}
@NonNull
private List> getDecodePaths(
@NonNull Class dataClass,//inputstream
@NonNull Class resourceClass,//object
@NonNull Class transcodeClass) {//Darwable
List> decodePaths = new ArrayList<>();
List> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass);//1
for (Class registeredResourceClass : registeredResourceClasses) {
List> registeredTranscodeClasses =
transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);//2
for (Class registeredTranscodeClass : registeredTranscodeClasses) {
List> decoders =
decoderRegistry.getDecoders(dataClass, registeredResourceClass);//3
ResourceTranscoder transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);//4
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
DecodePath path =
new DecodePath<>(
dataClass,
registeredResourceClass,
registeredTranscodeClass,
decoders,
transcoder,
throwableListPool);//5
decodePaths.add(path);
}
}
return decodePaths;
}
getDecodePaths方法根据参数从注册表中查询到所有符合条件的DecodePath集合。这里的参数分别是dataClass=InputStream.class resourceClass = Object.class transcodeClass = Drawable.class
注释1处decoderRegistry根据dataClass 和resourceClass 找到所有的资源类.如下是相关代码ResourceDecoderRegistry.java
...
@NonNull
@SuppressWarnings("unchecked")
public synchronized List> getResourceClasses(
@NonNull Class dataClass, @NonNull Class resourceClass) {
List> result = new ArrayList<>();
for (String bucket : bucketPriorityList) {
List> entries = decoders.get(bucket);
if (entries == null) {
continue;
}
for (Entry, ?> entry : entries) {
if (entry.handles(dataClass, resourceClass)
&& !result.contains((Class) entry.resourceClass)) {//去重
result.add((Class) entry.resourceClass);
}
}
}
return result;
}
...
private static class Entry {
public boolean handles(@NonNull Class> dataClass, @NonNull Class> resourceClass) {
return this.dataClass.isAssignableFrom(dataClass)
&& resourceClass.isAssignableFrom(this.resourceClass);//resourceClass是this.resourceClass基类或 者相同
}
}
这里的resourceClass 是Object.class,是所有类的基类,所以注册表中的所有类型都符合条件,由于前边去重的作用,所以registeredResourceClasses 集合中是GifDrawable.class Bitmap.class BitmapDrawable.class.
Registry#getDecodePaths方法的注释2处根据注册的资源类和要求的转码类(Drawable.class)
TranscoderRegistry#getTranscodeClasses方法
@NonNull
public synchronized List> getTranscodeClasses(
@NonNull Class resourceClass, @NonNull Class transcodeClass) {
List> transcodeClasses = new ArrayList<>();
// GifDrawable -> Drawable is just the UnitTranscoder, as is GifDrawable -> GifDrawable.
if (transcodeClass.isAssignableFrom(resourceClass)) {//1
transcodeClasses.add(transcodeClass);
return transcodeClasses;
}
for (Entry, ?> entry : transcoders) {
if (entry.handles(resourceClass, transcodeClass)) {//2
transcodeClasses.add(transcodeClass);//3
}
}
return transcodeClasses;
}
第一次GifDrawable.class 是 Drawable.class的子类,所以执行注释1,直接返回transcodeClasses 集合只有Drawable.class
第二次执行注释2,根据resourceClass, transcodeClass从注册表中查找transcodeClass,Glide中的注册表中条件满足的是
.register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources))
注意注释3处返回的是传进来的Drawable.class,所以第二次的集合只有一个是entry.resourceClass 即 Drawable.class
第三次BitmapDrawable.class.是Drawable.class的子类所以执行注释1,直接返回transcodeClasses 集合只有Drawable.class
接着是Registry#getDecodePaths方法的注释3的地方根据dataClass和注册的资源类从解码表中找到解码器集合。ResourceDecoderRegistry的代码如下:
/**
* 得到资源解码器
* @param dataClass 数据类(byteBuffer,stream等)
* @param resourceClass 资源类(bitmap,drawable等)
* @param T
* @param R
* @return 解码器集合
*/
@NonNull
@SuppressWarnings("unchecked")
public synchronized List> getDecoders(
@NonNull Class dataClass, @NonNull Class resourceClass) {
List> result = new ArrayList<>();
for (String bucket : bucketPriorityList) {
...
for (Entry, ?> entry : entries) {
if (entry.handles(dataClass, resourceClass)) {
result.add((ResourceDecoder) entry.decoder);
}
}
}
return result;
}
这里从Glide注册的资源解码器中查找符合条件的,结果如下:
第一次:Inputstream.class->GifDrawable.class 解码器是StreamGifDecoder对象
第二次:Inputstream.class->Bitmap.class 解码器是streamBitmapDecoder对象
第三次:Inputstream.class->BitmapDrawable.class 解码器是BitmapDrawableDecoder对象
Registry#getDecodePaths方法的注释4处根据注册的资源类和注册的解码类通过注册表查找资源转码器
ResourceTranscoder。TranscoderRegistry#get
@NonNull
@SuppressWarnings("unchecked")
public synchronized ResourceTranscoder get(
@NonNull Class resourceClass, @NonNull Class transcodedClass) {
if (transcodedClass.isAssignableFrom(resourceClass)) {//1 gifDrawable-Drawable
return (ResourceTranscoder) UnitTranscoder.get();
}
for (Entry, ?> entry : transcoders) {
if (entry.handles(resourceClass, transcodedClass)) {//2
return (ResourceTranscoder) entry.transcoder;
}
}
throw new IllegalArgumentException(
"No transcoder registered to transcode from " + resourceClass + " to " + transcodedClass);
}
for循环结果是:
第一次:GifDrawable->Drawable 满足注释1 得到是UnitTranscoder
第二次:Bitmap->Drawable 满足注释2 从注册表中找到的是BitmapDrawableTranscoder
第三次:BitmapDrawable->Drawable 满足注释1 得到是UnitTranscoder
getDecodePaths方法的注释5根据前边查询的参数组装DecodePath对象并且放入集合decodePaths中
所以总结一下最后的到的结果,伪代码是:
List> decodePaths = new ArrayList<>();
decodePaths .add(new DecodePath<>(
InputStream.class,
GifDrawable.class,
Drawable.class,
StreamGifDecoder.class,
UnitTranscoder.class,
throwableListPool);
decodePaths .add(new DecodePath<>(
InputStream.class,
Bitmap.class,
Drawable.class,
StreamBitmapDecoder.class,
BitmapDrawableTranscoder.class,
throwableListPool);
decodePaths .add(new DecodePath<>(
InputStream.class,
BitmapDrawable.class,
Drawable.class,
BitmapDrawableDecoder.class,
UnitTranscoder.class,
throwableListPool);
执行解码和编码
回到DecodeJob的runLoadPath注释1处查找DataRewinder 得到是InputStreamRewinder对象。然后是主要的load工作,即注释2处会执行LoadPath#loadWithExceptionList
private Resource loadWithExceptionList(
DataRewinder rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback decodeCallback,
List exceptions)
throws GlideException {
Resource result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);//1
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {//2
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
DecodePath#decode
public Resource decode(
DataRewinder rewinder,
int width,
int height,
@NonNull Options options,
DecodeCallback callback)
throws GlideException {
//解码资源
Resource decoded = decodeResource(rewinder, width, height, options);
//2 转换资源(CENTER_CROP等)
Resource transformed = callback.onResourceDecoded(decoded);
//3.转码(Bitmap=BitmapDrawable)
return transcoder.transcode(transformed, options);
}
解码资源的代码是:
@NonNull
private Resource decodeResource(
DataRewinder rewinder, int width, int height, @NonNull Options options)
throws GlideException {
List exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
@NonNull
private Resource decodeResourceWithList(
DataRewinder rewinder,
int width,
int height,
@NonNull Options options,
List exceptions)
throws GlideException {
Resource result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
...
}
if (result != null) {//3
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
根据前边的分析decoders第一个是StreamGifDecoder,它的decode得到的是null。
decoders第二个是StreamBitmapDecoder,它的decode的到result 是Bimap,即result!=null,所以注释3处结束循环,返回result。
转换 裁剪缩放成需要的样式的资源
DecodePath#decode的注释2处会回调decodeJob中的onResourceDecoded,方法内会对资源进行转换,并且把转换后的资源写入磁盘缓存中。
解码
DecodePath#decode的注释2处会将bitmap转为BitmapDrawable 资源类型
转码器BitmapDrawableTranscoder#transcode方法
@Nullable
@Override
public Resource transcode(
@NonNull Resource toTranscode, @NonNull Options options) {
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
LazyBitmapDrawableResource.java
@Nullable
public static Resource obtain(
@NonNull Resources resources, @Nullable Resource bitmapResource) {
if (bitmapResource == null) {
return null;
}
return new LazyBitmapDrawableResource(resources, bitmapResource);
}
private LazyBitmapDrawableResource(
@NonNull Resources resources, @NonNull Resource bitmapResource) {
this.resources = Preconditions.checkNotNull(resources);
this.bitmapResource = Preconditions.checkNotNull(bitmapResource);
}
最后通过转码得到的是LazyBitmapDrawableResource 资源类。加载的结果怎么处理,上一篇已经讲过了。