DecodeJob从磁盘获取数据后,需要解析decode数据
DecodeJob的onDataFetcherReady回调方法接收到磁盘数据,比如ByteBuffer
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
...
decodeFromRetrievedData();
...
}
}
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
//这是一个很重要的方法,需要具体详细分析
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
LoadPath<Data, ResourceType, R> path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
解析就进入了LoadPath类中
public Resource<Transcode> load(DataRewinder<Data> rewinder, @NonNull Options options, int width,
int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
List<Throwable> throwables = Preconditions.checkNotNull(listPool.acquire());
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
}
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;
//noinspection ForLoopReplaceableByForEach to improve perf
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类中
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
//decode过程
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
//transcode过程
return transcoder.transcode(transformed, options);
}
总结一下:从磁盘获取到的数据ByteBuffer,进过decode和transcode处理,decode过程变成Bitmap,封装成变成了BitmapResouce;transcode过程变成BitmapDrawable,封装成了LazyBitmapDrawableResource,只不过是lazy的,什么意思呢?其实LazyBitmapDrawableResource里面只是包含了BitmapResouce,需要的时候(get)才会生成BitmapDrawable。
在使用BitmapDrawable的时候有一个很重要的知识点是BitmapPool,这个以后分析。
我们看一下,获取到的Resource是怎么显示到ImageView上的
我们看decodeFromRetrievedData中调用的notifyEncodeAndRelease方法
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource<R> result = resource;
...
notifyComplete(result, dataSource);
...
}
private void notifyComplete(Resource<R> resource, DataSource dataSource) {
setNotifiedOrThrow();
callback.onResourceReady(resource, dataSource);
}
这里的callback是EngineJob,它实现了DecodeJob.Callback接口,我们在Glide之SourceGenerator数据请求和磁盘缓存文章中讲过这个接口的另一个方法reschedule,这个方法是用来调度线程的。
我们看EngineJob的onResourceReady回调方法:
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
if (isCancelled) {
// TODO: Seems like we might as well put this in the memory cache instead of just recycling
// it since we've gotten this far...
resource.recycle();
release();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
// Hold on to resource for duration of our callbacks below so we don't recycle it in the
// middle of notifying if it synchronously released by one of the callbacks. Acquire it under
// a lock here so that any newly added callback that executes before the next locked section
// below can't recycle the resource before we call the callbacks.
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
listener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
在分析上面代码前,我们先分析一下给EngineJob注册的各种监听。
我们先回顾和梳理一下请求流程:对于同一个上下文环境(同一个FragmentManager)有唯一的RequestManager,每次load都的时候都new 一个RequstBuilder,into的时候new 一个Request(也可能从对象池里面取),Request一般是SingleRequest,Requset去调用begin方法。注意啦!即便是load同一个地址,也是单独的RequestBuilder和SingleRequest。
在Request的begin方法中,最终会调用到统一引擎Engine的load方法,Engine是唯一的,注意看:
public synchronized void onSizeReady(int width, int height) {
...
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);
...
}
load的时候,最后两个参数,this表示的是SingleRequest,它实现了ResourceCallback接口,callbackExecutor是从RequestBuilder里面传入的,用于分发到主线程的Executor,实现如下
private static final Executor MAIN_THREAD_EXECUTOR =
new Executor() {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NonNull Runnable command) {
handler.post(command);
}
};
接着看Engine 的load方法:
public synchronized <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) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
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);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
//上面是从内存缓存中尝试去取数据,先忽略
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
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.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
load中分别获取了EngineJob和DecodeJob实例,注意看engineJob.addCallback(cb, callbackExecutor),cb就是SingleRequest,callbackExecutor就是分发到主线程的Executor。我们再看jobs.put(key, engineJob);这句话,这个是把key和对应的engineJob缓存起来,Jobs内部就是简单的HashMap,为啥要缓存呢?
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
看完这段代码,是不是就知道,为啥要缓存了呢?
其实是为了,如果连续对同一个地址发起请求时,没有必要每次都实例化EngineJob,只需要key一样缓存的同一个EngineJob就可以了,只是简单的加一组callback就可以了。
还有一个监听需要说明一下,就是EngineJob实例化的时候,Engine给它传了一个监听EngineJobListener,Engine实现了这个监听。
说完了各种监听是怎么进入EngineJob的,该回到主线了
void notifyCallbacksOfResult() {
ResourceCallbacksAndExecutors copy;
Key localKey;
EngineResource<?> localResource;
synchronized (this) {
stateVerifier.throwIfRecycled();
if (isCancelled) {
// TODO: Seems like we might as well put this in the memory cache instead of just recycling
// it since we've gotten this far...
resource.recycle();
release();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
// Hold on to resource for duration of our callbacks below so we don't recycle it in the
// middle of notifying if it synchronously released by one of the callbacks. Acquire it under
// a lock here so that any newly added callback that executes before the next locked section
// below can't recycle the resource before we call the callbacks.
hasResource = true;
copy = cbs.copy();
incrementPendingCallbacks(copy.size() + 1);
localKey = key;
localResource = engineResource;
}
listener.onEngineJobComplete(this, localKey, localResource);
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
decrementPendingCallbacks();
}
listener是Engine,看看onEngineJobComplete方法:
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null) {
resource.setResourceListener(key, this);
if (resource.isCacheable()) {
activeResources.activate(key, resource);
}
}
jobs.removeIfCurrent(key, engineJob);
}
把Resource缓存到activeResource内存里面
entry.executor.execute是把Runnable放到主线程中执行
private class CallResourceReady implements Runnable {
private final ResourceCallback cb;
CallResourceReady(ResourceCallback cb) {
this.cb = cb;
}
@Override
public void run() {
synchronized (EngineJob.this) {
if (cbs.contains(cb)) {
// Acquire for this particular callback.
engineResource.acquire();
callCallbackOnResourceReady(cb);
removeCallback(cb);
}
decrementPendingCallbacks();
}
}
}
callCallbackOnResourceReady是EngineJob的方法,在这个里面调用cb的方法,这个cb是SingleRequest,通过这么一处理,这个回调就到主线程中执行了。
还有一块很重要的内容,就是线程同步这块,我们以后再说
需要学习内容:
(1)BitmapDrawable中BitmapPool的使用
(2)EngineJob是怎么被实例化的?
(3)包在synchronized里面的代码和不包在synchronized的代码有什么区别?
(4)cbs.copy为啥要copy一份?
(5)activeResource缓存分析