工厂模式进阶之Android中工厂模式源码分析

Android工厂模式源码分析

本文对Android源码中所涉及到的工厂模式进行分析(源码不会涉及的具体的细节,具体细节读者请另查相关阅资料),最后再给出安卓中对工厂模式的应用场景案例。工厂模式总共三种:简单工厂、工厂方法、抽象工厂。

友情参考链接:

1、Android中简单工厂模式的体现

1.1、Fragment的创建

我们知道,Fragment之间传递数据有一种建议通过setArguments方法:

使用静态工厂方法,将外部传入的参数可以通过Fragment.setArgument保存在它自己身上,这样我们可以在Fragment.onCreate(…)调用的时候将这些参数取出来。

这样写有什么好处呢?

(1)、避免了在创建Fragment的时候无法在类外部知道所需参数的问题。

(2)、Fragment推荐使用setArguments来传递参数,避免在横竖屏切换的时候Fragment自动调用自己的无参构造函数,导致数据丢失。

1.2、Bitmap的创建过程

在Android中,我们经常使用静态工厂方法的地方应该是创建Bitmap对象的时候,例如通过资源id获取Bitmap对象。

那么我们看看BitmapFactory的工厂方法具体实现。

关注上面红笔标注的地方。可以看到,decodeResource(Resourcesres,intid)函数调用的是decodeResource(Resourcesres,intid, Options opts), 在decodeResource函数中,最终把传递进来的资源id解析成InputStream, 然后调用decodeResourceStream(res,value, is, null,opts)方法生成一个bitMap对象,最终是吧这个创建好的bitMap对象返回。这里的调用就是一个简单工厂模式的体现。看一下decodeResourceStream的实现(主要是对bitmap细节的创建):

上面代码我也不知道具体什么意思,可以关注一下红笔标注的地方。decodeResourceStream方法其实并没有去创建bitmap,而是初始化一些配置和像素信息,然后调用decodeStream(is,pad,opts),最终调用nativeDecodeAsset或者nativeDecodeStream来构建Bitmap对象,这两个都是native方法( Android中使用Skia库来解析图像 )。最后调用finishDecode函数来设置像素、配置信息、缩放等参数,最终返回Bitmap对象。BitmapFactory中的decodeFile、decodeByteArray工厂方法都是这么一个类似的过程,BitmapFactory通过不同的工厂方法与传递不同的参数调用不同的图像解析函数来构造Bitmap对象,符合简单工厂(静态工厂)模式的结构。

2、JAVA中工厂方法模式的体现

在Android的开发中,容器类通常是我们开发软件过程中不可缺少的基础组件,例如ArrayList, HashMap, HashSet等,而迭代容器中的元素是最常用的功能之一, 容器中的迭代器就是用了工厂方法设计模式(当然还有迭代器模式, 不在此讨论)。我们知道,不同的容器类型其内部数据结构是不同的。之前有说过,ArrayList代码结构简化版如下代码:

其中,Iterable就是抽象的工厂角色,里面有一个抽象方法

抽象方法返回值为抽象的产品角色:Iterator。

那么Arraylist就是具体的工厂角色,它用于创建具体的产品角色new Itr();

其中Itr实现了Iterator接口为具体的产品角色。

UML表示更明显:

除此之外其他集合也都是体现到了工厂方法模式,比如HashSet。其中HashSet为具体的工厂类,它里面同样返回创建迭代器类:

它是调用HashMap的keySet()方法,创建KeySet实例,底层使用到的是KeySet()的iterator()方法,发现本质上返回的是KeyIterator迭代器实例。

对于这里而言,Iterable作为了抽象的工厂类,定义了生产具体方法的接口:Iterator iterator();

HashSet为具体的工厂类,本质上生产的是HashMap中内部类KeySet的iterator()方法,在这里返回一个迭代器KeyIterator。而对于产品角色,KeyIterator就是具体的产品类,而对于Iterator是抽象的产品角色。

3、抽象工厂模式在安卓中的使用:

在源码中, 比较典型的抽象工厂模式的例子是java.sql包中的Connection类,在刚学习Java时我们都会学习使用JDBC链接数据库,代码大致是这样的:

上面我们是以MYSQL驱动为例,设置JDBC驱动以后,使用DriverManager.getConnection来获取具体的链接实现,然后通过这个Connection来创建一个Statement来提交SQL语句,Connection还可以创建clob, blob, sqlxml等对象:

即Connection就是抽象工厂,而具体的工厂实现则在不同的数据库驱动包种。

首先我们看DriverManager中的getConnection方法 :

红笔为核心逻辑。我们看到getConnection(String, String, String)函数调用了getConnection(Stringurl, java.util.Propertiesinfo,Classcaller)函数,在该函数中遍历以注册到DriverManager中的驱动,即registeredDrivers, 获取相应的驱动之后,链接到数据库,最后将该链接返回, 这样就获取到了具体的Connection, 代码为 :

那么MYSQL JDBC驱动是什么时候注册到DriverManager的呢 ?

我们看到在使用DriverManager之前,调用了以下这句代码 :

可以看到,上文中有一个静态语句块, 该语句块会在虚拟机第一次加载该类时首先执行, 该语句块的作用就是将Driver类的对象注册到DriverManager中:

可以看到注册后不就是上文中的registeredDrivers吗?他其实就是一个集合:

泛型指定为DriverInfo,上面也知道,是把数据库驱动包在了DriverInfo里面:

驱动的具体实现类为 NonRegisteringDriver。获取数据库驱动对象以后,我们需要调用驱动对象的connect(String, Properties)函数才能获取到Connection对象,我们看看 NonRegisteringDriver的connect(String, Properties)。

现在我们来理一下思路, java.sql包中的Statement, Clob, Blob, SQLXML都是扮演了抽象产品类族中的一员, 而java.sql.Connection则代表了抽象工厂类,里面有创建各个产品类的函数,具体的产品实现类、具体Connection工厂都封装在各个数据库驱动包中,通过Connection我们就可以创建Statement, Clob等同一类族中的对象。抽象与实现想分离,工厂可以创建一组相关的对象,客户代码使用较为简单。

为了更直观的认识java.sql.Connection为抽象的工厂,看一下他的部分代码如下:

以及整体大致的UML:

只不过在这里产品等级结构的实现都是一一对应。

4、抽象工厂在Android中的应用

本节内容以安卓中数据存储为例,使用抽象工厂模式设计数据存储方案。其中IOHandler表示抽象的产品角色,定义了数据存储的功能比如增删改查等,MemoryIoHandler和DiskIOHandler以及其他的数据存储(如SP等)代表了具体的产品角色;IOFactory定义了生产产品的功能,代表了抽象工厂角色;IOHandlerFactory则具体生产产品代表了具体的工厂角色。UML如下:

整体的代码就不贴上了,其中抽象工厂和具体工厂是抽象工厂模式的核心,代码如下:

IOFactory抽象工厂角色:

IOHandlerFactory具体工厂角色(用于创建不同的数据存储方案实例):

问题:上述数据存储方案,使用简单工厂、工厂方法模式该如何实现?

到此为止工厂模式在Android中体现相关的知识介绍完了。

下一篇:构建者模式进阶。

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20171230G07ILF00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券