前言
最近翻了草稿箱,看到了之前的一些草稿,有一些文章写的差不多了,但是后面没精力写或者因为其他原因就烂在草稿箱里了,现在决定把一些草稿拿出来补全或者重新写,也是温故知新的过程。
而这篇关于Fresco的分析文章,当初写的就十分蛋疼,因为Fresco的源码跳转链条太长了,内容太多,一篇文章实在写不下,所以最后放弃了,现在会分成几篇文章来谈。因此这篇文章就只探讨一个问题:
- 一张图片加载的完整过程究竟是怎样的?
走过这个过程,也就把Fresco主要的逻辑链条都走过了一遍,算是对Fresco有了一个比较清晰的认识了。
一个图片加载请求
一段Fresco代码
我们常写的一个Fresco图片加载代码可能是这样的:
ImageRequestBuilder imageRequestBuilder = ImageRequestBuilder.newBuilderWithSource("假装有一个图片url");
...
...
// 创建Controller
controller = controllerBuilder.setImageRequest(imageRequestBuilder.build()).build();
draweeview.setController(controller);
我们就从这里开始分析。
构建controller
我们先来看看DraweeController的创建,首先,我们通过ImageRequestBuilder来构造图片请求的相关信息
,然后设置到controller里面来创建一个有效的controller。
public AbstractDraweeController build() {
...
...
return buildController();
}
protected AbstractDraweeController buildController() {
...
...
AbstractDraweeController controller = obtainController();
...
...
return controller;
}
@Override
protected PipelineDraweeController obtainController() {
try {
// 尝试获取oldcontroller,
DraweeController oldController = getOldController();
PipelineDraweeController controller;
final String controllerId = generateUniqueControllerId();
if (oldController instanceof PipelineDraweeController) {
controller = (PipelineDraweeController) oldController;
} else {
controller = mPipelineDraweeControllerFactory.newController();
}
// 进行初始化配置
// 包含当前的图片加载请求的相关数据 id,缓存key,和Supplier数据提供者
controller.initialize(
// 创建一个数据提供者 为图片请求提供数据 (从缓存,网络中拿到数据)
obtainDataSourceSupplier(controller, controllerId),
controllerId,
getCacheKey(),
getCallerContext(),
mCustomDrawableFactories,
mImageOriginListener);
controller.initializePerformanceMonitoring(mImagePerfDataListener, this);
return controller;
} finally {
...
...
}
}
public void initialize(
Supplier>> dataSourceSupplier,
String id,
CacheKey cacheKey,
Object callerContext,
@Nullable ImmutableList customDrawableFactories,
@Nullable ImageOriginListener imageOriginListener) {
...
...
super.initialize(id, callerContext);
init(dataSourceSupplier);
...
...
}
// 给PipelineDraweeController设置数据源提供者对象,用于获取DataSource
private void init(Supplier>> dataSourceSupplier) {
mDataSourceSupplier = dataSourceSupplier;
}
我们再看看 obtainDataSourceSupplier(),确定到底创建的了一个什么类型的数据源提供者
AbstractDraweeControllerBuilder.java/PipelineDraweeCOntrollerBuilder.java
protected Supplier> obtainDataSourceSupplier(
final DraweeController controller, final String controllerId) {
...
...
if (mImageRequest != null) {
// 因为我们传入了imagerequest,所以会在这里创建Supplier
supplier = getDataSourceSupplierForRequest(controller, controllerId, mImageRequest);
} else if (mMultiImageRequests != null) {
supplier =
getFirstAvailableDataSourceSupplier(
controller, controllerId, mMultiImageRequests, mTryCacheOnlyFirst);
}
...
...
return supplier;
}
protected Supplier> getDataSourceSupplierForRequest(
final DraweeController controller,
final String controllerId,
final REQUEST imageRequest,
final CacheLevel cacheLevel) {
final Object callerContext = getCallerContext();
// 最终创建了一个Supplier的匿名内部类
return new Supplier>() {
@Override
public DataSource get() {
// 最终为Request提供了一个DataSource,用于订阅
//后面被调用时我们再来分析
return getDataSourceForRequest(
controller, controllerId, imageRequest, callerContext, cacheLevel);
}
....
...
};
}
上面代码跳来跳去,总结一句话就是build controller的过程就是配置好当前图片请求的上下文和请求对应的内容提供者的过程。
我们可以简单看一下DraweeController定义的接口
// 主要处理view相关的一些接口
public interface DraweeController {
@Nullable
DraweeHierarchy getHierarchy();
void setHierarchy(@Nullable DraweeHierarchy hierarchy);
// 处理view 的attach 和 dettch的回调
// 从而能很好处理图片的展示和释放
void onAttach();
void onDetach();
void onViewportVisibilityHint(boolean isVisibleInViewportHint);
boolean onTouchEvent(MotionEvent event);
Animatable getAnimatable();
// 判断图片请求是否一致
boolean isSameImageRequest(DraweeController other);
}
setController背后
接下来,我们看看draweeView.setcontroller()接口,如何触发图片请求,和把图片绘制到当前的view
DraweeView.java,
public void setController(@Nullable DraweeController draweeController) {
mDraweeHolder.setController(draweeController);
// 创建一个总的drawable绘制到view上,包含我们设置的imageholder
super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}
这里有一个mDraweeHolder,这在Fresco的DraweeView或者自定义View中是一个很关键的类,关于它的作用,我在之前的文章里面讲过,感兴趣的请移步从Fresco源码中找到非侵入式的答案。
这里简单说明一下:
[图片上传失败...(image-c4242d-1600530135587)]
DraweeViewHolder负责和Image Pipeline交互,获得图片数据,并更新到View中,我们先追踪图片请求的过程,绘制的部分后面再看。
public void setController(@Nullable DraweeController draweeController) {
boolean wasAttached = mIsControllerAttached;
if (wasAttached) {
detachController();
}
// Clear the old controller
if (isControllerValid()) {
mEventTracker.recordEvent(Event.ON_CLEAR_OLD_CONTROLLER);
mController.setHierarchy(null);
}
mController = draweeController;
if (mController != null) {
mEventTracker.recordEvent(Event.ON_SET_CONTROLLER);
mController.setHierarchy(mHierarchy);
} else {
mEventTracker.recordEvent(Event.ON_CLEAR_CONTROLLER);
}
if (wasAttached) {
// 一般情况下 view 已经是attched的状态,否则就会等attach的回调
attachController();
}
}
private void attachController() {
if (mIsControllerAttached) {
return;
}
mIsControllerAttached = true;
if (mController != null && mController.getHierarchy() != null) {
mController.onAttach();
}
}
AbstractDraweeController
@Override
public void onAttach() {
....
...
mIsAttached = true;
if (!mIsRequestSubmitted) {
submitRequest(); // 提交request
}
...
...
}
protected void submitRequest() {
final T closeableImage = getCachedImage();
if (closeableImage != null) {
// 如果有缓存,就使用缓存
// 我们暂且认为没有缓存
}
...
...
// 获取datasource
mDataSource = getDataSource();
...
...
// 终于看到了我们很熟悉的Image Pipline的使用方式
// 最终的数据都是通过这种观察者模式来通知和传递数据的
final DataSubscriber dataSubscriber =
new BaseDataSubscriber() {
@Override
public void onNewResultImpl(DataSource dataSource) {
// 最终获取到数据,然后开始传递到view中进行展示
// 这里我们先不展开,先追踪请求的逻辑,我们后面会回到这里
...
...
}
...
...
};
mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
....
...
}
上面submitRequest的主要工作就是创建一个观察者模式(订阅者模式),然后等待通知处理图片。而主要的工作显然丢给了被观察者,即DataSource,接下来我们看看getDataSource()里面的逻辑到底是怎样的?
PipelineDraweeController
@Override
protected DataSource> getDataSource() {
...
...
// 这里的mDataSourceSupplier 就是之前build controller这个步骤中,构造相关信息和逻辑时,
// 传递给PipelineDraweeController的,这个supplier就是一个匿名内部类,可翻到上面回顾一下
DataSource> result = mDataSourceSupplier.get();
...
...
return result;
}
// mDataSourceSupplier的对象就来源于这个方法,
// AbstractDraweeControllerBuilder.java
protected Supplier> getDataSourceSupplierForRequest(
final DraweeController controller,
final String controllerId,
final REQUEST imageRequest,
final CacheLevel cacheLevel) {
final Object callerContext = getCallerContext();
// 最终创建了一个Supplier的匿名内部类
return new Supplier>() {
// mDataSourceSupplier.get()调用的就是这个匿名内部类的get()方法
@Override
public DataSource get() {
// 因此这里获取的DataSource到底是什么吗
return getDataSourceForRequest(
controller, controllerId, imageRequest, callerContext, cacheLevel);
}
};
}
PipelineDraweeControllerBuilder.java
@Override
protected DataSource> getDataSourceForRequest(
DraweeController controller,
String controllerId,
ImageRequest imageRequest,
Object callerContext,
AbstractDraweeControllerBuilder.CacheLevel cacheLevel) {
return mImagePipeline.fetchDecodedImage(
imageRequest,
callerContext,
convertCacheLevelToRequestLevel(cacheLevel),
getRequestListener(controller),
controllerId);
}
ImagePipeline.java
// 提交图片的请求,并返回DataSource,用于订阅
public DataSource> fetchDecodedImage(
ImageRequest imageRequest,
Object callerContext,
ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit,
@Nullable RequestListener requestListener,
@Nullable String uiComponentId) {
try {
// 获取producer, Producer是image的数据生产者(数据提供者),用于从缓存或者网络中获取数据
Producer> producerSequence =
mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
return submitFetchRequest(
producerSequence,
imageRequest,
lowestPermittedRequestLevelOnSubmit,
callerContext,
requestListener,
uiComponentId);
} catch (Exception exception) {
return DataSources.immediateFailedDataSource(exception);
}
}
private DataSource> submitFetchRequest(
Producer> producerSequence,
ImageRequest imageRequest,
ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit,
Object callerContext,
@Nullable RequestListener requestListener,
@Nullable String uiComponentId) {
...
...
// 创建生产所需的上下文信息,即图片加载请求相关的信息
SettableProducerContext settableProducerContext =
new SettableProducerContext(
imageRequest,
generateUniqueFutureId(),
uiComponentId,
requestListener2,
callerContext,
lowestPermittedRequestLevel,
/* isPrefetch */ false,
imageRequest.getProgressiveRenderingEnabled()
|| !UriUtil.isNetworkUri(imageRequest.getSourceUri()),
imageRequest.getPriority(),
mConfig);
// 创建了一个CloseableProducerToDataSourceAdapter适配器,
// 用于适配producer和DataSource,
//也就是说producerSequence生产的数据需要通过适配,发送到DataSource
return CloseableProducerToDataSourceAdapter.create(
producerSequence, settableProducerContext, requestListener2);
}
简单看一下producer的接口定义
public interface Producer {
/**
* Start producing results for given context. Provided consumer is notified whenever progress is
* made (new value is ready or error occurs).
*
* @param consumer
* @param context
*/
// 根据生产上下文信息开始生产数据,然后通过consumer回调数据结果
void produceResults(Consumer consumer, ProducerContext context);
}
CloseableProducerToDataSourceAdapter一手从producer中拿到可用的图片数据,然后作为DataSource来通知它的订阅者(观察者)。
protected AbstractProducerToDataSourceAdapter(
Producer producer,
SettableProducerContext settableProducerContext,
RequestListener2 requestListener) {
...
...
// 这就是producer的生产数据的接口,即发起请求的接口
// 注意这个createConsumer() 这是数据最终的回调
producer.produceResults(createConsumer(), settableProducerContext);
}
// 创建回调的对象
private Consumer createConsumer() {
return new BaseConsumer() {
@Override
protected void onNewResultImpl(@Nullable T newResult, @Status int status) {
AbstractProducerToDataSourceAdapter.this.onNewResultImpl(newResult, status);
}
@Override
protected void onFailureImpl(Throwable throwable) {
AbstractProducerToDataSourceAdapter.this.onFailureImpl(throwable);
}
@Override
protected void onCancellationImpl() {
AbstractProducerToDataSourceAdapter.this.onCancellationImpl();
}
@Override
protected void onProgressUpdateImpl(float progress) {
AbstractProducerToDataSourceAdapter.this.setProgress(progress);
}
};
}
protected void onNewResultImpl(@Nullable T result, int status) {
boolean isLast = BaseConsumer.isLast(status);
// setResult会触发 notifyDataSubscribers() ,通知订阅者,数据已经准备好
if (super.setResult(result, isLast)) {
...
...
}
}
protected boolean setResult(@Nullable T value, boolean isLast) {
boolean result = setResultInternal(value, isLast);
if (result) {
notifyDataSubscribers();
}
return result;
}
现在还剩下一个问题,这个producer的实现是什么?我们回调上面获取producer的位置
ProducerSequenceFactory.java
public Producer> getDecodedImageProducerSequence(
ImageRequest imageRequest) {
...
...
Producer> pipelineSequence =
getBasicDecodedImageSequence(imageRequest);
return pipelineSequence;
}
private Producer> getBasicDecodedImageSequence(
ImageRequest imageRequest) {
Uri uri = imageRequest.getSourceUri();
// 根据uri 获取对应类型的producer
switch (imageRequest.getSourceUriType()) {
case SOURCE_TYPE_NETWORK:
// 因为我们传的是http链接,因此会返回 networkproducer
return getNetworkFetchSequence();
case SOURCE_TYPE_LOCAL_VIDEO_FILE:
return getLocalVideoFileFetchSequence();
case SOURCE_TYPE_LOCAL_IMAGE_FILE:
return getLocalImageFileFetchSequence();
case SOURCE_TYPE_LOCAL_CONTENT:
if (MediaUtils.isVideo(mContentResolver.getType(uri))) {
return getLocalVideoFileFetchSequence();
}
return getLocalContentUriFetchSequence();
case SOURCE_TYPE_LOCAL_ASSET:
return getLocalAssetFetchSequence();
case SOURCE_TYPE_LOCAL_RESOURCE:
return getLocalResourceFetchSequence();
case SOURCE_TYPE_QUALIFIED_RESOURCE:
return getQualifiedResourceFetchSequence();
case SOURCE_TYPE_DATA:
return getDataFetchSequence();
...
}
}
private synchronized Producer> getNetworkFetchSequence() {
if (mNetworkFetchSequence == null) {
mNetworkFetchSequence =
newBitmapCacheGetToDecodeSequence(getCommonNetworkFetchToEncodedMemorySequence());
}
return mNetworkFetchSequence;
}
// multiplex -> encoded cache -> disk cache -> (webp transcode) -> network fetch
private synchronized Producer getCommonNetworkFetchToEncodedMemorySequence() {
if (mCommonNetworkFetchToEncodedMemorySequence == null) {
// 这里最终创建了NetworkFetchProducer 用于最终从网络请求图片数据
// mNetworkFetcher 这个是fresco网络请求标准库,可替换,如不指定,会使用默认的网络请求库
Producer inputProducer =
newEncodedCacheMultiplexToTranscodeSequence(
mProducerFactory.newNetworkFetchProducer(mNetworkFetcher));
mCommonNetworkFetchToEncodedMemorySequence =
ProducerFactory.newAddImageTransformMetaDataProducer(inputProducer);
// 创建一些其他的Producer,对图片做其他处理,这里暂时不讲
mCommonNetworkFetchToEncodedMemorySequence =
mProducerFactory.newResizeAndRotateProducer(
mCommonNetworkFetchToEncodedMemorySequence,
mResizeAndRotateEnabledForNetwork && !mDownsampleEnabled,
mImageTranscoderFactory);
}
return mCommonNetworkFetchToEncodedMemorySequence;
}
我们看到的是创建的多个producer,关于这些producer的如何组织起来有序调用的呢?这里没有那么多篇幅了,简单来讲,它们的组织方式就是一环套一环
producers的整体结构
我们在上面追溯一个一个请求如何发出的时候,看到了很多Producer,他们的组织方式是一种类似与流水线模式,上层producer持有下层producer对象的引用,下层producer持有上层的回调Consumer ,
其基本结构示意图如下:
[图片上传失败...(image-7011d7-1600530135587)]
而一系列producer合起来,他们的结构是这样的:
这就是这一系列的producer的组织结构,我们可能会在下一篇文章里面详细分析这个。
从网络上请求图片
从CloseableProducerToDataSourceAdapter里首先调用了
// 开始生产数据
producer.produceResults(createConsumer(), settableProducerContext);
然后通过一系列合理组织起来的producer被有序调用,各种缓存的producer中找不到数据,最后会调用NetworkFetchProducer,做最后的处理。
NetworkFetchProducer
@Override
public void produceResults(Consumer consumer, ProducerContext context) {
context.getProducerListener().onProducerStart(context, PRODUCER_NAME);
final FetchState fetchState = mNetworkFetcher.createFetchState(consumer, context);
mNetworkFetcher.fetch(
fetchState,
new NetworkFetcher.Callback() {
@Override
public void onResponse(InputStream response, int responseLength) throws IOException {
NetworkFetchProducer.this.onResponse(fetchState, response, responseLength);
}
@Override
public void onFailure(Throwable throwable) {
NetworkFetchProducer.this.onFailure(fetchState, throwable);
}
@Override
public void onCancellation() {
NetworkFetchProducer.this.onCancellation(fetchState);
}
});
}
// 处理从网络上获取的数据
protected void onResponse(
FetchState fetchState, InputStream responseData, int responseContentLength)
throws IOException {
final PooledByteBufferOutputStream pooledOutputStream;
if (responseContentLength > 0) {
pooledOutputStream = mPooledByteBufferFactory.newOutputStream(responseContentLength);
} else {
pooledOutputStream = mPooledByteBufferFactory.newOutputStream();
}
final byte[] ioArray = mByteArrayPool.get(READ_SIZE);
try {
int length;
// 读取网络数据
while ((length = responseData.read(ioArray)) >= 0) {
if (length > 0) {
pooledOutputStream.write(ioArray, 0, length);
maybeHandleIntermediateResult(pooledOutputStream, fetchState);
float progress = calculateProgress(pooledOutputStream.size(), responseContentLength);
// 实时向上回调下载进度
fetchState.getConsumer().onProgressUpdate(progress);
}
}
mNetworkFetcher.onFetchCompletion(fetchState, pooledOutputStream.size());
// 处理获取到的图片数据
handleFinalResult(pooledOutputStream, fetchState);
} finally {
...
...
}
}
一个普通的请求,NetworkFetcher作为网络请求基础库的实现类常见的有两个HttpUrlConnectionNetworkFetcher(默认),OkHttpNetworkFetcher
我们看看完整的数据怎么回调到上层的
protected void handleFinalResult(
PooledByteBufferOutputStream pooledOutputStream, FetchState fetchState) {
Map extraMap = getExtraMap(fetchState, pooledOutputStream.size());
...
...
notifyConsumer(
pooledOutputStream,
Consumer.IS_LAST | fetchState.getOnNewResultStatusFlags(),
fetchState.getResponseBytesRange(),
fetchState.getConsumer(),
fetchState.getContext());
}
protected static void notifyConsumer(
PooledByteBufferOutputStream pooledOutputStream,
@Consumer.Status int status,
@Nullable BytesRange responseBytesRange,
Consumer consumer,
ProducerContext context) {
CloseableReference result = CloseableReference.of(pooledOutputStream.toByteBuffer());
EncodedImage encodedImage = null;
try {
// 从网络图片编码数据中获取一些信息
encodedImage = new EncodedImage(result);
encodedImage.setBytesRange(responseBytesRange);
// 如果你点进去看fresco解析图片信息,你会发现fresco亲切告诉你这里有一个bug
encodedImage.parseMetaData();
context.setEncodedImageOrigin(EncodedImageOrigin.NETWORK);
// 开始把整合的数据传递回调给上层
// consumer就是上层的回调
consumer.onNewResult(encodedImage, status);
} finally {
...
...
}
}
从底层NetworkFetchProducer开始回调,中间会经过各种producer会做一些加工处理(下篇文章再讲),然后一直回调到最上层的AbstractProducerToDataSourceAdapter的createConsumer()这里,然后通过setResult()来通知订阅者,完成数据获取的的通知。
我们再把上面的订阅者的回调方法写在下面,看看得到的图片数据如何绘制到view上的。
...
...
// 获取datasource
mDataSource = getDataSource();
final DataSubscriber dataSubscriber =
new BaseDataSubscriber() {
@Override
public void onNewResultImpl(DataSource dataSource) {
// 图片回调到此处
boolean isFinished = dataSource.isFinished();
boolean hasMultipleResults = dataSource.hasMultipleResults();
float progress = dataSource.getProgress();
T image = dataSource.getResult();
if (image != null) {
// 最终处理数据
onNewResultInternal(
id, dataSource, image, progress, isFinished, wasImmediate, hasMultipleResults);
}
...
...
}
...
...
};
private void onNewResultInternal(
String id,
DataSource dataSource,
@Nullable T image,
float progress,
boolean isFinished,
boolean wasImmediate,
boolean deliverTempResult) {
try {
...
...
Drawable drawable;
try {
// 通过image 创建drawable
drawable = createDrawable(image);
} catch (Exception exception) {
...
...
}
T previousImage = mFetchedImage;
Drawable previousDrawable = mDrawable;
mFetchedImage = image;
mDrawable = drawable;
try {
// 设置新的图片
if (isFinished) {
logMessageAndImage("set_final_result @ onNewResult", image);
mDataSource = null;
// 把封装了图片的drawable传递到DraweeHierarchy 准备绘制
mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);
getControllerListener().onFinalImageSet(id, getImageInfo(image), getAnimatable());
}
...
...
} finally {
// 处理之前的drawable
if (previousDrawable != null && previousDrawable != drawable) {
releaseDrawable(previousDrawable);
}
if (previousImage != null && previousImage != image) {
logMessageAndImage("release_previous_result @ onNewResult", previousImage);
releaseImage(previousImage);
}
}
} finally {
...
...
}
}
我们得到了image,封装成drawable,把数据传递给DraweeHierarchy去更新drawable,这也印证了我们上面的结构划分。
我们继续去DraweeHierarchy的唯一实现类 GenericDraweeHierarchy里面看看,最后怎么处理这个drawable的。
GenericDraweeHierarchy.java
// TopLevelDrawable 就是DraweeHolder最先设置到View中展示的Drawable
// 内部可以绘制多个层级的Drawable
private final RootDrawable mTopLevelDrawable;
// ForwardingDrawable 就是TopLevelDrawable内其中一个层级的drawable,负责绘制请求的图片
private final ForwardingDrawable mActualImageWrapper;
@Override
public void setImage(Drawable drawable, float progress, boolean immediate) {
...
...
// ForwardingDrawable setDrawable 开始更新自己
// GenericDraweeHierarchy 内部通过TopLevelDrawable承载了很多layer Drawable
// ForwardingDrawable就是其中一个layer drawable,
mActualImageWrapper.setDrawable(drawable);
...
...
}
到这里,我们终于完成了一个图片发出请求,到绘制到view 上的完整过程。
总结
我们回顾一下,主要有两个操作
- build DraweeController
- setcontroller
第一个操作,主要的作用就是配置该图片的相关信息和执行逻辑
第二个操作则是把图片连接和对应的view绑定,保证图片能绘制到对应的view,并创建一系列producer,有序执行和回调。
勘误
暂无