在前面的文章 Fresco 源码分析 —— 图片加载流程 里面详细说明了图片加载的整个流程,但是除了理解源码之外,对于源码的框架层面的设计也是需要去了解的,不能只是简单的读源码,好的源码的框架设计也是值得我们去学习的。以后,我们自己在开发一个源码的时候,也就能将学到的好的经验运用到自己的代码上。

代码工程

从 module 层面来看,可以看到 Fresco 是有很多 module 的,并且这些 module 都是按照每个 module 的功能进行划分的,可以通过名字就可以知道 module 的作用。

虽然 module 很多,但是并不像我们平时的项目里面一样,一个 module 包含很多代码,有的可能只有几个类,只是 fresco 整体分的比较细。

下面介绍下一些关键的 module:

  • animated-base:主要都是动图的一些共有的基础操作,包阔每一帧的缓存,解码渲染,帧数,宽高等逻辑。

  • Drawee:就是 UI 层,例如 DraweeView, drawable 相关的类等;

  • imagepipeline:整个工程的核心,图片加载,内存、缓存管理,bitmap处理等等核心逻辑;

  • imagepipeline-base:是 imagepipeline 的一些基础类,包括接口(缓存,解码,图片信息)以及相关的基础类;

  • imagepipeline-backends :这里就是最后的请求逻辑,这里给出两个不同的示例,分别采用 volly 和 ok-http 来实现的,默认是 HttpUrlConnectionNetworkFetcher ,也就是说业务方有着更复杂的业务需求的话,需要自己去实现。

  • Drawee-backends:主要是在 Drawee 的基础上封装了请求和初始化逻辑,比如 Fresco,PipelineDraweeController 等相关类。

设计思想浅谈

1、里面有很多类以 config 结尾,他们是怎么用的?

ImagePipelineConfig:内部采用 builder 模式来进行创建,主要是把 ImagePipeline 需要的参数都通过 config 来进行管理;

ImagePipelineFactory: 通过 ImagePipelineConfig 来生成 ImagePipeline ;

类似的还有:

  • DraweeConfig: 内部采用 builder 模式来进行创建,包含 Drawee 相关的配置;

  • DiskCacheConfig:内部采用 builder 模式来进行创建,包含 disk 的各种配置,包括目录,版本,缓存大小,错误日志等。最后也是使用 DiskStorageCacheFactory 来生成 disk;

  • PoolConfig:内部采用 builder 模式来进行创建,其实最终也是再 PoolFactory 里面使用;

可以发现,Fresco 里面的 config 类都是采用了 buidler 模式。那为啥需要采用 builder 模式呢?因为 config 从名字来说是配置类,里面会有很多参数,所以会采用 builder 模式,以后别人在配置的时候,就不会关心过的参数问题。

其实对于内部 builder 我有个疑问就是为啥要使用内部 builder 。

  • 一是内部属性太多,如果采用构造函数模式,那需要写很多构造函数

  • 二是采用builder 模式后,用户只需要设置他关心的属性,其他不关心的属性都可以采用默认值来进行处理,也就是减轻了使用者的压力。

  • 三是一旦构造完成,就不可以修改了,builder 里面都是设置属性,但是类本身只提供获取属性方法,不提供设置方法,隔绝了用户更改带来的不可控因素。

  • 至于内部builder 可能是不希望将他们独立出去,散落在各处不好管理。

2、提供了很多 producer,consumer,那这么多类是如何管理的,他们之间恶关系如何维护。

提供了 ProducerFactory 来管理所有的 producer。ProducerFactory 有静态方法,大多数是非静态方法。主要用来获取各种 producer 。

ProducerSequenceFactory: 这个其实就是把各个 Producer 连接在一起;或者说是按照一定的规则,将他们组装在一起。这样外界在调用的时候,只需要确定你的 Sequence 是什么样的,调用对应的方法 获取 Sequence。其中在 Sequence 里面又会通过 ProducerFactory 来获取指定的

每一个 producer 又会有一个对应的 consumer,可以发现大多数 consumer 都是 producer 里面的内部类。

3、DataSource 的作用

DataSource 是一个泛型接口。按照源码的描述,它和 future 原理差不多,但是有个不一样的地方,就是它可以获取当前的进度。

AbstractDataSource 继承自 DataSource;这块内部已经维护好了各种状态;然后会通过 listeners 进行通知。

AbstractProducerToDataSourceAdapter : 继承自 AbstractDataSource,从名字就可以看出来这是一个适配器,将 Producer 转为 DataSource。

4、ProducerContext 的作用

主要是用来将上下文信息传递给 Producer;可以具体看看代码,可以发现 context 内部包含很多逻辑。

public interface ProducerContext {

  @StringDef({
ExtraKeys.ORIGIN,
ExtraKeys.ORIGIN_SUBCATEGORY,
ExtraKeys.NORMALIZED_URI,
ExtraKeys.SOURCE_URI,
ExtraKeys.ENCODED_WIDTH,
ExtraKeys.ENCODED_HEIGHT,
ExtraKeys.ENCODED_SIZE,
ExtraKeys.MULTIPLEX_BITMAP_COUNT,
ExtraKeys.MULTIPLEX_ENCODED_COUNT,
})
@interface ExtraKeys {
String ORIGIN = "origin";
String ORIGIN_SUBCATEGORY = "origin_sub";
String SOURCE_URI = "uri_source";
String NORMALIZED_URI = "uri_norm";
String ENCODED_WIDTH = "encoded_width";
String ENCODED_HEIGHT = "encoded_height";
String ENCODED_SIZE = "encoded_size";
/* number of deduped request in BitmapMemoryCacheKeyMultiplexProducer */
String MULTIPLEX_BITMAP_COUNT = "multiplex_bmp_cnt";
/* number of deduped request in EncodedCacheKeyMultiplexProducer */
String MULTIPLEX_ENCODED_COUNT = "multiplex_enc_cnt";
} /** @return image request that is being executed */
ImageRequest getImageRequest(); /** @return id of this request */
String getId(); /** @return optional id of the UI component requesting the image */
@Nullable
String getUiComponentId(); /** @return ProducerListener2 for producer's events */
ProducerListener2 getProducerListener(); /** @return the {@link Object} that indicates the caller's context */
Object getCallerContext(); /** @return the lowest permitted {@link ImageRequest.RequestLevel} */
ImageRequest.RequestLevel getLowestPermittedRequestLevel(); /** @return true if the request is a prefetch, false otherwise. */
boolean isPrefetch(); /** @return priority of the request. */
Priority getPriority(); /** @return true if request's owner expects intermediate results */
boolean isIntermediateResultExpected(); /**
* Adds callbacks to the set of callbacks that are executed at various points during the
* processing of a request.
*
* @param callbacks callbacks to be executed
*/
void addCallbacks(ProducerContextCallbacks callbacks); ImagePipelineConfig getImagePipelineConfig(); EncodedImageOrigin getEncodedImageOrigin(); void setEncodedImageOrigin(EncodedImageOrigin encodedImageOrigin); <E> void setExtra(@ExtraKeys String key, @Nullable E value); void putExtras(@Nullable Map<String, ?> extras); @Nullable
<E> E getExtra(String key); @Nullable
<E> E getExtra(String key, @Nullable E valueIfNotFound); Map<String, Object> getExtras(); /** Helper to set {@link ExtraKeys#ORIGIN} and {@link ExtraKeys#ORIGIN_SUBCATEGORY} */
void putOriginExtra(@Nullable String origin, @Nullable String subcategory); /** Helper to set {@link ExtraKeys#ORIGIN} */
void putOriginExtra(@Nullable String origin);
}

这里说下 ProducerContext 使用,这里其实采用的是接口模式,然后相当于是面向接口编程,这样后期在扩展的时候,也会变得更加方便。

类似的上下文还有 FrescoContext。

其实对于上下文,我们在设计源码的时候,也可以考虑下,有了 context 的存在,可以减少很多类之间的依赖,使得代码逻辑更加清晰。

5、ImagePipeline

ImagePipelineConfig: 这个可以说是把 ImagePipeline 用到的东西一网打尽;这个是用于用户在使用 Fresco 的时候,可以进行对应的配置。

ImagePipeline: 发起请求的类(包括网络,本地缓存,内存,回调)以及解码和非解码的图片,还有包括预取;

ImagePipelineFactory: 是一个单例。也就是说明所有的请求配置是一样的。但是这样的话,怎么区分不同的请求,这块还需要仔细看看的。url 是通过 imageRequest 来管理的,ImagePipeline 主要是负责管理其他方面的东西。包括缓存等所有请求应该都是一样的。然后在获取 getDataSourceSupplier 的时候发起图片请求。

imageRequest:imageRequest 包含 url 等相关信息。会在 ImagePipeline 中构造一个 producerSequence。最终,producerSequence 和 settableProducerContext 会在 AbstractProducerToDataSourceAdapter 转变为 DataSource;

6、ImagePipeline 和 producer 之间的关系

这个其实在 ImagePipeline 中解释过了,他们相当于是一环套一环。

7、builder 模式的使用

ImageRequestBuilder: 用于构建 ImageRequest。

AbstractDraweeControllerBuilder: 使用得是泛型,将通用逻辑封装在其内部;

PipelineDraweeControllerBuilder:controller 的逻辑在里面;

这个其实主要是在前面的 config 里面讲过了,这里就不再细说。

8、factory 模式的应用

DefaultDrawableFactory:生成动态静态图片;

PipelineDraweeControllerFactory:一个是创建controller,一个是创建内部controller;

ImagePipelineFactory:这个有点感觉是个容器,所有和 ImagePipeline 相关的都可以从里面获取到;

其他 factory 的类也有很多。就我个人理解,之所以用到 factory ,主要是为了屏蔽产品的具体实现,调用者只关心产品的接口。

 

浅谈 Fresco 框架结构的更多相关文章

  1. 浅谈Spring MVC知识

    关于MVC框架,我相信大家都不陌生,都会说也就是模型-视图-控制器这三层的框架结构,如果你参加面试的时候考官会问:“MVC框架是什么?你说一说.”其实我们都知道这个问题还需要问的,只要你是一个开发人员 ...

  2. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

  3. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

  4. 浅谈Java的throw与throws

    转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...

  5. 浅谈SQL注入风险 - 一个Login拿下Server

    前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...

  6. 浅谈WebService的版本兼容性设计

    在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...

  7. 浅谈angular2+ionic2

    浅谈angular2+ionic2   前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别.   1. 项目所用:angular2+ionic2 ...

  8. iOS开发之浅谈MVVM的架构设计与团队协作

    今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  9. Linux特殊符号浅谈

    Linux特殊字符浅谈 我们经常跟键盘上面那些特殊符号比如(?.!.~...)打交道,其实在Linux有其独特的含义,大致可以分为三类:Linux特殊符号.通配符.正则表达式. Linux特殊符号又可 ...

随机推荐

  1. 多种转弯角度的PBN旁切转弯图例分析

    无论世界怎样变化,我们依然是有点阳光就灿烂.面对世界的未知,最好的状态是勇敢的去面对,努力的去生活. 今天我们继续来聊一下PBN旁切转弯. PBN转弯保护区的结构通常都与它们的转弯角度大小有关,转弯角 ...

  2. 观点纠正,yarn和npm对比,今天yarn仍然比npm快吗

    yarn和npm的区别和对比,网上很多了,不多说了. 只纠正一个观点:yarn仍然比npm快吗?不. 2016年,yarn刚刚发布,速度确实比npm快,于是网络上出现了好多推荐yarn的文章. 于是很 ...

  3. jetty的jndi

    jetty的jndi和tomcat的用法 tomcat的jndi是内置的,在web.xml文件里直接默认支持的,所有web项目可以直接使用 <resources> <!-- < ...

  4. linux驱动系列之程序反汇编

    摘抄网页:http://www.169it.com/article/330129798173630299.html 参考网页:http://www.cppblog.com/liu1061/articl ...

  5. mysql explain type详解

    本文转载自最官方的 mysql explain type 字段解读 读了很多别人的笔记都杂乱不堪,很少有实例,什么都不如原装的好,所以当你读到我的笔记的时候如果觉得说的不明白,最好参考官方的手册. 我 ...

  6. 在js中如何将字符串类型的日期("2020-11-30T02:21:42.000+0000")进行格式化

    1.引入方法 import { formatDateNew } from '@/utils' 2.在方法中使用,注意要先将字符串进行new Date(),否则报错date.getFullYear is ...

  7. Go | Go 结合 Consul 实现动态反向代理

    Go 结合 Consul 实现动态反向代理 代理的核心功能可以用一句话概括:接受客户端的请求,转发到后端服务器,获得应答之后返回给客户端. Table of Contents 反向代理 实现逻辑 Go ...

  8. Mac创建Root用户

    1.打开Mac终端管理工具 前往-实用工具-终端 2.用命令的形式创建账户 sudo passwd root 3.输入当前登录用户密码 4.输入root用户密码并验证

  9. 手把手教你Spring Boot2.x整合Elasticsearch(ES)

    文末会附上完整的代码包供大家下载参考,码字不易,如果对你有帮助请给个点赞和关注,谢谢! 如果只是想看java对于Elasticsearch的操作可以直接看第四大点 一.docker部署Elastics ...

  10. Python2021哔哩哔哩视频爬取

    一.找到想要爬取的视频,进入网页源代码 在网页源代码里面可以很容易的找到视频各种清晰度的源地址 二.对地址发送请求 如果对视频源地址发送get请求会返回403 通过按F12进入开发者工具分析 发现并不 ...