摘要:如果你是第一次看我的的源码分析系列文章,这里强烈推荐你先阅读我的前面两篇文章源码分析之与源码分析之。其中是用来管理推迟资源释放的。发送预处理请求,获取相应类型的缓存数据。当数据源已经获取到时,发送通知给订阅者,因此分别回调订阅者的方法。
如果你是第一次看我的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, MemoryCachememoryCache, @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 DataSubscriberdataSubscriber = 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, DataSourcedataSource, @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...
阅读 2957·2021-09-10 10:50
阅读 3136·2019-08-30 14:19
阅读 3476·2019-08-29 17:31
阅读 3202·2019-08-29 16:43
阅读 2155·2019-08-29 14:05
阅读 2061·2019-08-29 13:17
阅读 2015·2019-08-26 13:25
阅读 1718·2019-08-26 12:20