若是你是第一次看个人Fresco的源码分析系列文章,这里强烈推荐你先阅读个人前面两篇文章Fresco源码分析之DraweeView与Fresco源码分析之Hierarchy。好了,下面进入正题。在上篇文章中咱们提到,在Fresco中关于图片的缓存、请求与显示逻辑处理都在Controller中。那么Controller究竟是如何贯穿这些功能的呢?咱们先从它的出生开始。php
PipelineDraweeControllerBuilderSupplier是一个供应商,主要实现了Supplier<T>
接口,它只有一个方法T get(),用来获取相关的提供实现。所以该供应类提供的就是PipelineDraweeControllerBuilder实例。java
@Override
public PipelineDraweeControllerBuilder get() {
return new PipelineDraweeControllerBuilder(
mContext,
mPipelineDraweeControllerFactory,
mImagePipeline,
mBoundControllerListeners);
}
复制代码
在生成的builder中有4个参数,第一个是Context再熟悉不过了;第二个是Controller的工厂;第三个是数据管道ImagePipeline;第四个是listener的set集合,主要用在图片请求以后的监听回调。下面详细说明后面三个参数内容与做用。android
在这个类中主要就两个方法,分别为internalCreateController与newController,对外的方法就一个newController。这个两个方法都是用来建立PipelineDraweeController对象。其中newController内部就是调用了internalCreateController来进行建立PipelineDraweeController实例。git
protected PipelineDraweeController internalCreateController(
Resources resources,
DeferredReleaser deferredReleaser,
DrawableFactory animatedDrawableFactory,
Executor uiThreadExecutor,
MemoryCache<CacheKey, CloseableImage> memoryCache,
@Nullable ImmutableList<DrawableFactory> globalDrawableFactories,
@Nullable ImmutableList<DrawableFactory> customDrawableFactories,
Supplier<DataSource<CloseableReference<CloseableImage>>> 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集合的惟一性的特性)。github
dataSourceSupplier是DataSource的供应商,用来提供DataSource实例。而DataSource是用来获取与存储请求结果的,至关与图片数据源。这些都会在后续的Controller中使用到。缓存
既然它是数据管道,天然是与网络请求与缓存数据有关。其实咱们能够把它理解为多个管道的集合,最终显示的图片资源就是来自于它们中的其中一个。下面介绍其中的主要方法:bash
这里用的最多的仍是fetchDecodedImage()微信
public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(
ImageRequest imageRequest,
Object callerContext,
ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) {
try {
Producer<CloseableReference<CloseableImage>> producerSequence =
mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
return submitFetchRequest(
producerSequence,
imageRequest,
lowestPermittedRequestLevelOnSubmit,
callerContext);
} catch (Exception exception) {
return DataSources.immediateFailedDataSource(exception);
}
}
复制代码
这里主要涉及到Producer,这是一个生产者,内部只有一个公共接口方法void produceResults(Consumer consumer, ProducerContext context),用来获取数据源。其实咱们会发现submitFetchRequest方法中的producer传入的实际上是一个队列,由于内部会递归调用*produceResults()*来获取最终的数据源。网络
关于Producer后续有时间的话会单独开篇文章详细分析。ide
若是你对ControllerListener不熟悉的话,那么BaseControllerListener应该或多或少使用过吧。它其实就是ControllerListener的空实现。既然是监听回调,那么来看下它提供的回调方法与调用时机。
既然是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<DataSource<IMAGE>> getDataSourceSupplierForRequest(
final REQUEST imageRequest,
final CacheLevel cacheLevel) {
final Object callerContext = getCallerContext();
return new Supplier<DataSource<IMAGE>>() {
@Override
public DataSource<IMAGE> 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<CloseableReference<CloseableImage>> getDataSourceForRequest(
ImageRequest imageRequest,
Object callerContext,
CacheLevel cacheLevel) {
return mImagePipeline.fetchDecodedImage(
imageRequest,
callerContext,
convertCacheLevelToRequestLevel(cacheLevel));
}
复制代码
看到上面的方法实现方法是否眼熟呢?这也是咱们上面所提到的ImagePipleline中的方法,这里就不在多作分析了。这样Controller就与获取数据的通道创建了联系。那么下面咱们就转战到Controller中,看看它到底作了什么。
PipelineDraweeController继承于AbstractDraweeController,在PipelineDraweeController中主要的方法有三个
其他的逻辑处理都在它的父类AbstractDraweeController中。在以前咱们屡次说起到onAttach与onDetach方法,它们分别是处理数据加载与释放。
@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<T> dataSubscriber =
new BaseDataSubscriber<T>() {
@Override
public void onNewResultImpl(DataSource<T> 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<T> dataSource) {
//失败处理
onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
}
@Override
public void onProgressUpdate(DataSource<T> 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<T> 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()*这里就不贴出源码来进行分析了。原理与调用的方法基本相似,若是想看源码的能够点这里
@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。
本篇文章主要分析了Fresco中的Controller相关处理逻辑,它控制着Hierarchy显示逻辑,同时它是数据源的获取桥梁经过DataSource来连接数据源的获取。那么问题又来了,DataSource又是如何产生的呢?同时它的内部逻辑又是如何的呢?这就涉及到Producer了,敬请关注下篇文章Fresco源码分析之Producer