摘要:如果你是第一次看我的的源码分析系列文章,这里强烈推荐你先阅读我的前面两篇文章源码分析之与源码分析之。其中是用来管理推迟资源释放的。发送预处理请求,获取相应类型的缓存数据。当数据源已经获取到时,发送通知给订阅者,因此分别回调订阅者的方法。
如果你是第一次看我的Fresco的源码分析系列文章,这里强烈推荐你先阅读我的前面两篇文章Fresco源码分析之DraweeView与Fresco源码分析之Hierarchy。好了,下面进入正题。在上篇文章中我们提到,在Fresco中关于图片的缓存、请求与显示逻辑处理都在Controller中。那么Controller到底是如何贯穿这些功能的呢?我们先从它的出生开始。
SuppilerPipelineDraweeControllerBuilderSupplier是一个供应商,主要实现了Supplier
@Override
public PipelineDraweeControllerBuilder get() {
return new PipelineDraweeControllerBuilder(
mContext,
mPipelineDraweeControllerFactory,
mImagePipeline,
mBoundControllerListeners);
}
在生成的builder中有4个参数,第一个是Context再熟悉不过了;第二个是Controller的工厂;第三个是数据管道ImagePipeline;第四个是listener的set集合,主要用在图片请求之后的监听回调。下面详细说明后面三个参数内容与作用。
PipelineDraweeControllerFactory在这个类中主要就两个方法,分别为internalCreateController与newController,对外的方法就一个newController。这个两个方法都是用来创建PipelineDraweeController对象。其中newController内部就是调用了internalCreateController来进行创建PipelineDraweeController实例。
protected PipelineDraweeController internalCreateController(
Resources resources,
DeferredReleaser deferredReleaser,
DrawableFactory animatedDrawableFactory,
Executor uiThreadExecutor,
MemoryCache memoryCache,
@Nullable ImmutableList globalDrawableFactories,
@Nullable ImmutableList customDrawableFactories,
Supplier>> dataSourceSupplier,
String id,
CacheKey cacheKey,
Object callerContext) {
PipelineDraweeController controller = new PipelineDraweeController(
resources,
deferredReleaser,
animatedDrawableFactory,
uiThreadExecutor,
memoryCache,
dataSourceSupplier,
id,
cacheKey,
callerContext,
globalDrawableFactories);
controller.setCustomDrawableFactories(customDrawableFactories);
return controller;
}
其中DeferredReleaser是用来管理推迟资源释放的。我们在之前的文章已经提到,在onAttach中会进行加载资源,而onDetach中又会释放资源。因为在Fresco中往往会在onDetach与onAttach之间频繁切换(view的显隐、绘制与Controller的设置都会调用),并且它们都处于在同一个looper(其实就是主进程的looper)中。如果在onDetach时马上释放资源的话,这样会造成资源的滥用,导致不必要的资源加载与释放回收。所以就用了这个资源推迟释放的机制(内部原理是使用了set集合的唯一性的特性)。
dataSourceSupplier是DataSource的供应商,用来提供DataSource实例。而DataSource是用来获取与存储请求结果的,相当与图片数据源。这些都会在后续的Controller中使用到。
ImagePipeline既然它是数据管道,自然是与网络请求与缓存数据有关。其实我们可以把它理解为多个管道的集合,最终显示的图片资源就是来自于它们中的其中一个。下面介绍其中的主要方法:
fetchDecodedImage() 发送请求,返回decode image的数据源。
fetchEncodedImage() 发送请求,返回encoded image的数据源。
prefetchToBitmapCache() 发送预处理请求,获取预处理的bitmap缓存数据。
prefetchToDiskCache() 发送预处理请求,获取预处理的磁盘缓存数据。
submitFetchRequest() 发送请求,获取相应类型的数据源。
submitPrefetchRequest() 发送预处理请求,获取相应类型的缓存数据。
这里用的最多的还是fetchDecodedImage()
public DataSource> fetchDecodedImage( ImageRequest imageRequest, Object callerContext, ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) { try { Producer > producerSequence = mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest); return submitFetchRequest( producerSequence, imageRequest, lowestPermittedRequestLevelOnSubmit, callerContext); } catch (Exception exception) { return DataSources.immediateFailedDataSource(exception); } }
这里主要涉及到Producer,这是一个生产者,内部只有一个公共接口方法void produceResults(Consumer
关于Producer后续有时间的话会多带带开篇文章详细分析。ControllerListener
如果你对ControllerListener不熟悉的话,那么BaseControllerListener应该或多或少使用过吧。它其实就是ControllerListener的空实现。既然是监听回调,那么来看下它提供的回调方法与调用时机。
void onSubmit(String id, Object callerContext) 在发送请求的时候回调
void onFinalImageSet(String id, @Nullable INFO imageInfo, @Nullable Animatable animatable) 在最终设置image图片时回调,其中imageInfo包含图片的相关基本信息(width、height与quality)
void onIntermediateImageSet(String id, @Nullable INFO imageInfo) 在发送请求与最终图片设置的过程中回调
void onIntermediateImageFailed(String id, Throwable throwable) 在发送请求与最终失败的过程中回调
void onFailure(String id, Throwable throwable) 发送请求失败时回调
void onRelease(String id) 资源释放时回调
ControllerBuilder既然是builder模式,最终的目的自然就是用来创建Controller,所以我们可以直接奔着它的目的来分析。在这里Controller的builder类是PipelineDraweeControllerBuilder。我们找到它的build()发现在它的父类AbstractDraweeControllerBuilder中。但最终的创建实例方法还是调用了obtainController()抽象方法。所以经过反转还是回到了PipelineDraweeControllerBuilder,那么我们直接来看下它创建方式。
@Override
protected PipelineDraweeController obtainController() {
DraweeController oldController = getOldController();
PipelineDraweeController controller;
if (oldController instanceof PipelineDraweeController) {
controller = (PipelineDraweeController) oldController;
controller.initialize(
obtainDataSourceSupplier(),
generateUniqueControllerId(),
getCacheKey(),
getCallerContext(),
mCustomDrawableFactories);
} else {
controller = mPipelineDraweeControllerFactory.newController(
obtainDataSourceSupplier(),
generateUniqueControllerId(),
getCacheKey(),
getCallerContext(),
mCustomDrawableFactories);
}
return controller;
}
通过上面的代码,逻辑已经很明显了。首先判断是否已经存在Controller,如果存在的话就无需创建新的实例,只需调用initialize()方法进行重写初始化;如果不存在,那么就调用我们文章之前分析的PipelineDraweeControllerFactory中的newController()来创建新的实例。这里主要的参数还是obtainDataSourceSupplier(),之前也简单提到了,它是DataSource的供应者。那么我们来看下Supplier的创建
protected Supplier> getDataSourceSupplierForRequest( final REQUEST imageRequest, final CacheLevel cacheLevel) { final Object callerContext = getCallerContext(); return new Supplier >() { @Override public DataSource get() { return getDataSourceForRequest(imageRequest, callerContext, cacheLevel); } @Override public String toString() { return Objects.toStringHelper(this) .add("request", imageRequest.toString()) .toString(); } }; }
在这个方法中,我们一眼就看到了Supplier的创建,之前也提到它只有一个get()方法,就是用来提供所以需要的DataSource。在这里也是如此,这里它调用了getDataSourceForRequest()方法,该方法是一个抽象方法,细节实现由它的子类实现,所以我们可以再次回到getDataSourceForRequest,在其中就能够搜索到getDataSourceForRequest()方法
@Override protected DataSource> getDataSourceForRequest( ImageRequest imageRequest, Object callerContext, CacheLevel cacheLevel) { return mImagePipeline.fetchDecodedImage( imageRequest, callerContext, convertCacheLevelToRequestLevel(cacheLevel)); }
看到上面的方法实现方法是否眼熟呢?这也是我们上面所提到的ImagePipleline中的方法,这里就不在多做分析了。这样Controller就与获取数据的通道建立了联系。那么下面我们就转战到Controller中,看看它到底做了什么。
ControllerPipelineDraweeController继承于AbstractDraweeController,在PipelineDraweeController中主要的方法有三个
Drawable createDrawable(CloseableImage closeableImage) 这是内部类DrawableFactory中的方法,是一个工厂,不言而喻它是用来创建Drawable的,在数据源返回的时候回调,进而显示到Hierarchy层。
getDataSource() 获取数据源通道,与其建立联系。
void setHierarchy(@Nullable DraweeHierarchy hierarchy) 设置Hierarchy图层,内部持有的其实是SettableDraweeHierarchy接口对象。所以内部调用的也就是它的6个接口方法。之前的文章也有提及,用来控制图片加载过程中的显示逻辑。
其余的逻辑处理都在它的父类AbstractDraweeController中。在之前我们多次提及到onAttach与onDetach方法,它们分别是处理数据加载与释放。
onAttach @Override
public void onAttach() {
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(
TAG,
"controller %x %s: onAttach: %s",
System.identityHashCode(this),
mId,
mIsRequestSubmitted ? "request already submitted" : "request needs submit");
}
//事件记录器
mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
Preconditions.checkNotNull(mSettableDraweeHierarchy);
//取消资源推迟释放机制,防止资源被释放
mDeferredReleaser.cancelDeferredRelease(this);
mIsAttached = true;
if (!mIsRequestSubmitted) {
submitRequest();
}
}
在这个方法中mEventTracker是事件记录器,默认是开启的,如果要关闭则需要在Fresco.initialize()之前调用DraweeEventTracker.disable()关闭;然后就是将其从资源推迟释放机制中取消;最后就是调用submitRequest()发送数据源请求。
protected void submitRequest() {
final T closeableImage = getCachedImage();
//1.判断内存缓存中是否存在
if (closeableImage != null) {
mDataSource = null;
mIsRequestSubmitted = true;
mHasFetchFailed = false;
mEventTracker.recordEvent(Event.ON_SUBMIT_CACHE_HIT);
//1.1数据获取中通知回调
getControllerListener().onSubmit(mId, mCallerContext);
//1.2数据处理
onNewResultInternal(mId, mDataSource, closeableImage, 1.0f, true, true);
return;
}
mEventTracker.recordEvent(Event.ON_DATASOURCE_SUBMIT);
//2.通过DataSource获取数据源
//2.1数据获取中通知回调
getControllerListener().onSubmit(mId, mCallerContext);
mSettableDraweeHierarchy.setProgress(0, true);
mIsRequestSubmitted = true;
mHasFetchFailed = false;
mDataSource = getDataSource();
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(
TAG,
"controller %x %s: submitRequest: dataSource: %x",
System.identityHashCode(this),
mId,
System.identityHashCode(mDataSource));
}
final String id = mId;
final boolean wasImmediate = mDataSource.hasResult();
//内部请求数据回调,当数据源返回时回调
final DataSubscriber dataSubscriber =
new BaseDataSubscriber() {
@Override
public void onNewResultImpl(DataSource dataSource) {
// isFinished must be obtained before image, otherwise we might set intermediate result
// as final image.
boolean isFinished = dataSource.isFinished();
float progress = dataSource.getProgress();
T image = dataSource.getResult();
//2.2数据处理
if (image != null) {
//成功处理
onNewResultInternal(id, dataSource, image, progress, isFinished, wasImmediate);
} else if (isFinished) {
//失败处理
onFailureInternal(id, dataSource, new NullPointerException(), /* isFinished */ true);
}
}
@Override
public void onFailureImpl(DataSource dataSource) {
//失败处理
onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
}
@Override
public void onProgressUpdate(DataSource dataSource) {
boolean isFinished = dataSource.isFinished();
float progress = dataSource.getProgress();
//数据进度处理
onProgressUpdateInternal(id, dataSource, progress, isFinished);
}
};
//数据源订阅回调注册
mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
}
逻辑方面的处理,上面代码中已经详细注释了,总的来说就是先从内存中获取如果存在就直接拿来用,否则就通过DataSource从网络或者是本地资源中获取。使用DataSource方式会使用到DataSubscriber,即订阅方式。当数据源已经获取到时,发送通知给订阅者,因此分别回调订阅者的方法。上述两种方式只要成功了都会交由onNewResultInternal()处理,而失败则由onFailureInternal()处理,同时请求进度处理由onProgressUpdateInternal()处理。
private void onNewResultInternal(
String id,
DataSource dataSource,
@Nullable T image,
float progress,
boolean isFinished,
boolean wasImmediate) {
// ignore late callbacks (data source that returned the new result is not the one we expected)
if (!isExpectedDataSource(id, dataSource)) {
logMessageAndImage("ignore_old_datasource @ onNewResult", image);
releaseImage(image);
dataSource.close();
return;
}
mEventTracker.recordEvent(
isFinished ? Event.ON_DATASOURCE_RESULT : Event.ON_DATASOURCE_RESULT_INT);
// create drawable
Drawable drawable;
try {
drawable = createDrawable(image);
} catch (Exception exception) {
logMessageAndImage("drawable_failed @ onNewResult", image);
releaseImage(image);
onFailureInternal(id, dataSource, exception, isFinished);
return;
}
T previousImage = mFetchedImage;
Drawable previousDrawable = mDrawable;
mFetchedImage = image;
mDrawable = drawable;
try {
// set the new image
if (isFinished) {
logMessageAndImage("set_final_result @ onNewResult", image);
mDataSource = null;
//通过hierarchy(GenericDraweeHierarchy)来设置image
mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);
getControllerListener().onFinalImageSet(id, getImageInfo(image), getAnimatable());
// IMPORTANT: do not execute any instance-specific code after this point
} else {
logMessageAndImage("set_intermediate_result @ onNewResult", image);
mSettableDraweeHierarchy.setImage(drawable, progress, wasImmediate);
getControllerListener().onIntermediateImageSet(id, getImageInfo(image));
// IMPORTANT: do not execute any instance-specific code after this point
}
} finally {
if (previousDrawable != null && previousDrawable != drawable) {
releaseDrawable(previousDrawable);
}
if (previousImage != null && previousImage != image) {
logMessageAndImage("release_previous_result @ onNewResult", previousImage);
releaseImage(previousImage);
}
}
}
这里我们主要就看里面的两个try。
第一个使用createDrawable(image)将拿到的数据源转变成Drawable,这个方法的具体实现是在子类中实现(上面也有提及)。
第二个分为两种情况,一方面如果数据源已经全部获取完,则直接调用SettableDraweeHierarchy接口的setImage()方法将图片设置到Hierarchy图层上,同时调用Listener的回调方法onFinalImageSet();另一方面如果数据源还在获取中,也是调用SettableDraweeHierarchy接口的setImage()方法,只是其中的参数progress根据进度来设置而已,由于还处于资源获取中所以调用onIntermediateImageSet()回调。
这样Controller就与Hierarchy联系起来了,将需要的图片设置到显示的图片中。
对于SettableDraweeHierarchy中的这些方法如果不理解的可以回过头去看我之前的这篇文章Fresco源码分析之Hierarchy
由于源码太多,对于onFailureInternal()与onProgressUpdateInternal()这里就不贴出源码来进行分析了。原理与调用的方法基本类似,如果想看源码的可以点这里
onDetach @Override
public void onDetach() {
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(TAG, "controller %x %s: onDetach", System.identityHashCode(this), mId);
}
mEventTracker.recordEvent(Event.ON_DETACH_CONTROLLER);
mIsAttached = false;
mDeferredReleaser.scheduleDeferredRelease(this);
}
相对于之前的分析onDetach()就简单多了,这里它只是对资源进行释放,释放的策略也是推迟释放策略DeferredReleaser。
End本篇文章主要分析了Fresco中的Controller相关处理逻辑,它控制着Hierarchy显示逻辑,同时它是数据源的获取桥梁通过DataSource来链接数据源的获取。那么问题又来了,DataSource又是如何产生的呢?同时它的内部逻辑又是如何的呢?这就涉及到Producer了,敬请关注下篇文章Fresco源码分析之Producer
Fresco源码分析系列Github地址
关注 RecommendFresco源码分析之DraweeView
Fresco源码分析之Hierarchy
Android共享动画兼容实现
Kotlin最佳实践
RecyclerView下拉刷新与上拉更多
Android高仿微信之mvp实现(四)
tensorflow-梯度下降,有这一篇就足够了
博客
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68076.html
摘要:首先这是对的源码分析,所以在看这篇文章之前你应该要有使用的基础,如果没有的强烈推荐看下官方文档。在中统一由来替代。关于后续文章会详细分析。在其内部的,是用来记录事件的传递,方便的调试。这次主要是分析了中的基本组件与它的子类。 在Android中图片加载的框架很多,例如:Fresco、Picasso、Glide与Imageloader。它们都有各自的优点,但总的来说,使用起来方便简单、可...
摘要:最终的显隐操作都会转化为在方法中进行操作。主要是通过与数组来控制数组中各个的值,即显隐它继承于,顾名思义通过矩阵来改变状态。 上篇文章我们分析了Fresco中的DraweeView,对其中的一些原理以及方法进行了解析。在这过程中我们了解到,DraweeView中是通过DraweeHolder来统一管理的。而DraweeHolder又是用来统一管理相关的Hierarchy与Control...
阅读 3198·2021-09-10 10:50
阅读 3385·2019-08-30 14:19
阅读 3677·2019-08-29 17:31
阅读 3523·2019-08-29 16:43
阅读 2385·2019-08-29 14:05
阅读 2271·2019-08-29 13:17
阅读 2189·2019-08-26 13:25
阅读 1999·2019-08-26 12:20