Fresco正传(2):DraweeView分析
2015-11-12 18:15
375 查看
正文
既然要分析DraweeView,那么就先看一下
DraweeView的主要集成体系,然后在详细分析一下都做了些什么事情。
在看潘永强博客的时候,其中他提到:源码的分析分为广度和深度,先广度再深度,但是都要适当,避免陷入无止境的广度和深度的细节中去。
从这么多篇博文来看,源码的分析从上至下、先父类后子类、先构造再细节的分析思路是非常棒的。但是,每个人分析习惯不一样,就我来说更喜欢先从更易接触的类再深入父类、先看类注释再看详细的方法。后续的博客也会基本按照此种思路。
SimpleDraweeView是官方文档中,我们最先接触类,也是最易使用的类。那么就从这个类入手,一步一步分析。先上一下继承体系图,虽不是从上至下的分析,但是也需要充分了解体系结构。
好的开发框架,在命名方面往往做的非常好,起到见名知意作用。那么,我们就从自己的开发经验推测一下每个类都做了什么。
SimpleDraweeView是简单控件的意思,既然是简单,就会提供出简单明了、便易使用的方法,我想
setImageURI(Uri uri)就是这样一个方法。
GenericDraweeView是通用控件的意思,通过上一篇文章知道,
DraweeView体系中,持有了
DraweeHierarchy和
DraweeController的引用,那么默认的东西,会不会是在这个类中提供的?。
DraweeView是什么意思呢?,只从命名来看推测不出来什么。
SimpleDrawee
要分析一个类,就先看他的类注释。
/** * This view takes a uri as input and internally builds and sets a controller. * This class must be statically initialized in order to be used. * If you are using the Fresco * image pipeline, use Fresco#initialize to do this. */
这个控件接收URI作为输入,并在内部建立和设置一个控制器。
这个类必须被静态初始化才能够被使用,如果你想使用Fresco默认的图片管道来加载图片,请使用Fresco的initlialize去做这件事。
相关注释一定会体现为代码表示,我们很轻松就能够找到做第一件事情的代码:
/** * Displays an image given by the uri. */ public void setImageURI(Uri uri, @Nullable Object callerContext) { DraweeController controller = mSimpleDraweeControllerBuilder .setCallerContext(callerContext) .setUri(uri) .setOldController(getController()) .build(); setController(controller); }
通过uri展示一张图片。在其内部,通过
mSimpleDraweeControllerBuilder建造了一个
DraweeController对象,并通过
setController()方法,将控制器设置给了顶层的
DraweeView类,当前从目前来看是这个样子的。而一般以
Builder结尾的都是使用了建造者模式的类。
这是一个学习设计模式的博客。
mSimpleDraweeControllerBuilder是一个成员变量,搜索一下它在哪里被赋值的。
来到了
init()方法,此方法被各个构造函数所调用。
private void init() { if (isInEditMode()) { return; } Preconditions.checkNotNull(sDraweeControllerBuilderSupplier, "SimpleDraweeView was not initialized!"); mSimpleDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get(); }
虽然,代码不多,还是值得解释一下。
isInEditMode()是判断当前是否是预览模式或者是编辑模式,通常在AndrdioStudio中预览布局时,会碰到自定义控件显示不出来的情况,加上这句话就可以了。
而
Preconditions.checkNotNull()是Fresco的工具类,从命名来看,就是检查相关引用是否不为空,为空则直接报错.
下面一句则是,从
Supplier(提供者、供应商)中获取一个
SimpleDraweeContoller的建造器。特别说明的是,
Supplier虽然不是某种设计模式之一,但是在Fresco被广泛使用,它可是提供单个对象类型的类,在语义上它可能是一个工厂、建造、或其他任意的东西。
sDraweeControllerBuilderSupplier是一个静态成员变量,它的初始化就联系到了本类所做的第二件事,使用
Fresco.initialize(Context)来初始化本类。
既然需要适当在广度上扩展,就接着看一下
Fresco类。根据类的注释,可以知道:
这个类是Fresco的切入点。
在使用这个类之前,你必须初始化此类。而一种简单初始化的方式就是调用
Fresco.initialize(Context)。
既然有简单的,那么就有非简单的,一共有两种方式:
/** Initializes Fresco with the default config. */ public static void initialize(Context context) { ImagePipelineFactory.initialize(context); initializeDrawee(context); } /** Initializes Fresco with the specified config. */ public static void initialize(Context context, ImagePipelineConfig imagePipelineConfig) { ImagePipelineFactory.initialize(imagePipelineConfig); initializeDrawee(context); }
其中提到了默认的配置和指定的配置,这里不做过多的深入,对于指定的配置(也就两个参数的方法),你只需要知道在
ImagePipelineConfig中有着大量的参数配置,以保证Fresco框架的可配置化,由于内部参数过多使用了建造者模式来解耦代码。
见名知意,
ImagePipelineFactory.initialize(context)就是对配置初始化,这里就先略过了。
这样就引申到了
initializeDrawee(Context context)方法:
private static void initializeDrawee(Context context) { sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context); SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier); }
这段代码非常重要,它决定了Fresco使用的默认控制器是名为:
PipelineDraweeController的类,先简略看一下类注释,该Controller是图片加载器与Model的桥梁,也就是说,如何使用
ImagePipeline加载图片,并交给Model都是在这个类中控制的。当然,这也符合MVC模式的设计。
Drawee controller that bridges the image pipeline with {@link SettableDraweeHierarchy}.
接着就调用到了
SimpleDraweeView静态初始化的方法。这样,从
Fresco初始化到
SimpleDraweeView的初始化至调用
setImageUri()显示一张图片的流程就基本完整了。
给出一个示意图,这个图既非UML图、也非流程图,仅仅表达一些代码过程,个人觉得使用图形更形象一些:
GenericDraweeView
从类注释入手可以了解,本类的功能主要是通过解析XML的属性来构建出一个通用的
Hierarchy并进行设置。此外,还提供了一个设置控件宽高比的方法。
本类比较简单,核心的方法在于
inflateHierarchy(Context context, @Nullable AttributeSet attrs)。
方法只做的三件事:
1. 解析XML属性
2. 使用建造者模式构建
GenericDraweeHierarchy
3. 将
Hierarchy设置给
DraweeView体系
private void inflateHierarchy(Context context, @Nullable AttributeSet attrs) { Resources resources = context.getResources(); // 解析XML属性 if (attrs != null) { } // 使用建造者模式,构建Hierarchy GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(resources); // ... ... // 为DraweeView体系设置hierarchy setHierarchy(builder.build()); }
到此,值得注意的是。与
DraweeView有关的
DraweeHierarchy和
DraweeController均已出现,
DraweeHierarchy对应的默认实现类是:
GenericDraweeHierarchy;
DraweeController对应的默认实现类是:
PipelineDraweeController。
此外,本类做的第二件事,请参考博文:控制宽高比
DraweeView
先上一张图,更简洁的表达目的和想做的事。还是先从类的注释入手吧。从注释中可以了解到如下几件事:
该控件用于展示从
DraweeHierarchy获取的图像。
使用此控件之前,应该先为其设置
DraweeHierarchy。由于创建一个
DraweeHierarchy是一个昂贵的操作,推荐在创建的时候只做一次。
为了展示一张图片,
DraweeController必须被设置。
Note:虽然这个控件是ImageView的直接子类,但是不支持ImageView的众多方法。
在这个类中,即将接触到一个其背后的男人
DraweeHolder类,可以说
DraweeView类中所做的所有操作,都是直接或者间接和
DraweeHolder有关系的。
DraweeView从其体系中获取到的
DraweeHierarchy和
DraweeController的信息都被其转交到了
DraweeHolder类中。其实,这个就是一个解耦操作。假设,你想要自定一个控件,只想使用
DraweeHierarchy和
DraweeController的功能,难道还自己持有这两个类的引用? Just So So。
DraweeHolder帮你省去了很多麻烦。
先看一下
DraweeHolder是如何被创建的。
private void init(Context context) { ... mDraweeHolder = DraweeHolder.create(null, context); ... }
看样子平平无偿,浏览该类的其他方法你会发现,大部分的操作都是将信息转交给
DraweeHolder的操作。但是,请不要忽略以下两个方法:
public void setController(@Nullable DraweeController draweeController) { mDraweeHolder.setController(draweeController); // 重点 super.setImageDrawable(mDraweeHolder.getTopLevelDrawable()); } public void setHierarchy(DH hierarchy) { mDraweeHolder.setHierarchy(hierarchy); // 重点 super.setImageDrawable(mDraweeHolder.getTopLevelDrawable()); }
注意到了两个重点没?
mDraweeHolder.getTopLevelDrawable()获取到的就是
DraweeHierarchy.getTopLevelDraweeable()。
从之前的分析可以得知,
setHierarchy()是先被调用而后才是
setController(),
通过
super.setImageDrawable(mDraweeHolder.getTopLevelDrawable())这句话,就将Fresco的图片显示与ImageView结合了起来,这也是图片能够显示的原因。
如果你自定义控件了,千万不要忘记模仿这两个方法加上同样的语句哦。
既然,
DraweeView已经将职责都转交给了
DraweeHolder类,那么就来看看
DraweeHolder类做了些什么。
还是先从类注释入手:
这个类持有了
DraweeController和
DraweeHierarchy
这个类便于自定义控件使用
在浏览类的所有方法时,
onAttach()和
onDetach()方法引起了我的注意。
public void onAttach() { mEventTracker.recordEvent(Event.ON_HOLDER_ATTACH); mIsHolderAttached = true; attachOrDetachController(); } public void onDetach() { mEventTracker.recordEvent(Event.ON_HOLDER_DETACH); mIsHolderAttached = false; attachOrDetachController(); }
通过注释可以知道
onAttach()的核心作用是获取
DraweeController以显示图像;
onDetach()的核心作用是释放用于显示图像的资源。他们都共同调用了
attachOrDetachController()方法。
private void attachOrDetachController() { if (mIsHolderAttached && mIsVisible && mIsActivityStarted) { attachController(); } else { detachController(); } }
你会发现,当同时满足
onAttach()被调用、控件可见时、Activity启动时才会调用
attachController()方法,既然需要同时满足这么多条件,想来这一定是个非常重要的方法。
可以说,Fresco核心加载的逻辑都是由这段代码引出的。
private void attachController() { if (mIsControllerAttached) { return; } mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER); mIsControllerAttached = true; if (mController != null && mController.getHierarchy() != null) { mController.onAttach(); } } private void detachController() { if (!mIsControllerAttached) { return; } mEventTracker.recordEvent(Event.ON_DETACH_CONTROLLER); mIsControllerAttached = false; if (mController != null) { mController.onDetach(); } }
其中
mIsControllerAttached是保证核心逻辑在一个控件中只被执行一次。在
attachController()方法中,如果同时满足了
DraweeController不为空和
DraweeHierarchy不为空,才会调用
DraweeController的
onAttach()方法。
到了这里,你是不是非常好奇在
DraweeController的
onAttach()的方法中,到底做了怎么样重要的逻辑? (^__^) 嘻嘻……
最后
DraweeView的体系分析就基本完成了,如果觉得对您有帮助,多多留言哦。github:https://github.com/biezhihua
总览:http://blog.csdn.net/biezhihua/article/details/49783817
相关文章推荐
- User setting file does not exist C:\Users\lenevo\.m2\setting.xml 问题解决
- 我所认识的Android(一)
- loadrunner工具的组成
- jquery的promise实践--连续加载图片
- [LeetCode]Roman to Integer
- 真爱旅社官网详见
- 时间格式化的format.setLenient(false)
- Mouse Picking with Ray Casting
- JSP状态管理之cookie
- 杭州润泰 4s 坑人 黑店
- python中实现定制类的特殊方法总结
- LruCache和DiskLruCache优化网络异步加载图片
- JDBC(4)
- 实验二、作业调度模拟程序实验
- 1111《操作系统教程》实验二 作业调度模拟程序
- 数据结构学习笔记
- CStatic重载使用滚动条
- Android开发学习笔记:浅谈显示Intent和隐式Intent
- Yii-数据模型- rules类验证器方法详解
- ahjesus在asp.net中还可以通过设置HttpCookie对象的过期时间为DateTime.MinValue来指定此Cookies为跟随浏览器生效