Android源码中的静态工厂方法
来源:互联网 发布:python crypto key 编辑:程序博客网 时间:2024/05/11 03:23
我们知道工厂模式有三兄弟,通常我们说的工厂模式指的是工厂方法模式,它的应用频率最高。本篇博客分享的简单工厂模式是工厂方法模式的“小弟”,确切的来讲它不属于设计模式,而是一种方法。此外,工厂方法模式还有一位“大哥”——抽象工厂模式。
今天我们来分享一下简单工厂模式的一些情况,以及它在Android源码中的应用。
简单工厂模式
定义
简单工厂模式是类的创建模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
结构
简单工厂模式所涉及到的角色:
- Product(抽象产品角色):产品的通用接口,定义产品的行为。
- ConcreteProduct(具体产品角色):具体产品类,实现了Product接口。
- Creator(工厂角色):工厂类,通过静态工厂方法factoryMethord来创建对象。
实现
抽象产品角色
abstract class Product { //所有产品类的公共业务方法 public void methodSame() { //公共方法的实现 } //声明抽象业务方法 public abstract void methodDiff(); }
具体产品角色
class ConcreteProduct extends Product { //实现业务方法 public void methodDiff() { //业务方法的实现 } }
工厂角色
class Creator { //静态工厂方法 public static Product getProduct(String arg) { Product product = null; if (arg.equalsIgnoreCase("A")) { product = new ConcreteProductA(); //初始化设置product } else if (arg.equalsIgnoreCase("B")) { product = new ConcreteProductB(); //初始化设置product } return product; } }
使用场景
在以下情况下可以考虑使用简单工厂模式:
- 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。
优点
工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅“消费”产品,简单工厂模式实现了对象创建和使用的分离。
客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以在一定程度减少使用者的记忆量。
缺点
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
Android中简单工厂模式的应用
在Android中我们了解的使用到了简单工厂方法的地方有Bitmap对象的获取、Fragment创建等。接下来我们分开看一下。
Bitmap源码分析
首先来说我们是不能通过new方法来创建Bitmap对象的,因为Bitmap类的构造函数是私有的,只能是通过JNI实例化。
接下来我们随便找个入口开始看,比如:
Bitmap bmp = BitmapFactory.decodeFile(String pathName);
我们把源码中的调用关系找出来,如下
public static Bitmap decodeFile(String pathName) { return decodeFile(pathName, null);}public static Bitmap decodeFile(String pathName, Options opts) { Bitmap bm = null; InputStream stream = null; try { stream = new FileInputStream(pathName); bm = decodeStream(stream, null, opts); } catch (Exception e) { /* do nothing. If the exception happened on open, bm will be null. */ Log.e("BitmapFactory", "Unable to decode stream: " + e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // do nothing here } } } return bm;}public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) { // we don't throw in this case, thus allowing the caller to only check // the cache, and not force the image to be decoded. if (is == null) { return null; } Bitmap bm = null; Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); try { if (is instanceof AssetManager.AssetInputStream) { final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset(); bm = nativeDecodeAsset(asset, outPadding, opts); } else { bm = decodeStreamInternal(is, outPadding, opts); } if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); } setDensityFromOptions(bm, opts); } finally { Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } return bm;}private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage, Rect padding, Options opts);/** * Set the newly decoded bitmap's density based on the Options. */private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) { if (outputBitmap == null || opts == null) return; final int density = opts.inDensity; if (density != 0) { outputBitmap.setDensity(density); final int targetDensity = opts.inTargetDensity; if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) { return; } byte[] np = outputBitmap.getNinePatchChunk(); final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); if (opts.inScaled || isNinePatch) { outputBitmap.setDensity(targetDensity); } } else if (opts.inBitmap != null) { // bitmap was reused, ensure density is reset outputBitmap.setDensity(Bitmap.getDefaultDensity()); }}
我们来分析一下调用过程,可以看到decodeFile(String pathName)调用的是decodeFile(String pathName, Options opts),在两个参数的decodeFile方法中又去调用了decodeStream(InputStream is, Rect outPadding, Options opts)方法,然后最终调用nativeDecodeAsset或者nativeDecodeStream来构建Bitmap对象,这两个都是native方法(Android中使用Skia库来解析图像 )。再经过setDensityFromOptions方法的一些设置解码密度之类的操作,返回我们要的Bitmap对象。
/**
* Creates Bitmap objects from various sources, including files, streams, and byte-arrays.
*/
看下BitmapFactory的注释我们可以看到,这个工厂支持从不同的资源创建Bitmap对象,包括files, streams, 和byte-arrays,但是调用关系都大同小异。
Fragment创建
有时候,为了简化简单工厂模式,我们可以将抽象产品类和工厂类合并,将静态工厂方法移至抽象产品类中。Fragment的创建使用简单工厂方法没有抽象产品类,所以工厂类放到了实现产品类中。
在AndroidStudio中输入newInstance会自动补全Fragment的简单工厂方法。
public static TasksFragment newInstance() { Bundle args = new Bundle(); TasksFragment fragment = new TasksFragment(); fragment.setArguments(args); return fragment;}
使用静态工厂方法,将外部传入的参数可以通过Fragment.setArgument保存在它自己身上,这样我们可以在Fragment.onCreate(…)调用的时候将这些参数取出来。
这样写有什么好处呢?
避免了在创建Fragment的时候无法在类外部知道所需参数的问题。
Fragment推荐使用setArguments来传递参数,避免在横竖屏切换的时候Fragment自动调用自己的无参构造函数,导致数据丢失。
- Android源码中的静态工厂方法
- Android 源码中的静态工厂方法
- Android 源码中的工厂方法模式
- 工厂方法模式及在Android源码中的应用
- Android中的工厂方法模式
- Android源码分析之工厂方法模式
- 静态工厂 工厂方法 抽象工厂
- 静态工厂方法
- 静态工厂方法模式
- 静态工厂方法
- 静态工厂方法
- 静态工厂方法
- 静态工厂方法
- Java 静态工厂方法
- 静态工厂方法
- Java 静态工厂方法
- 静态工厂方法
- java 静态工厂方法
- 查找100以内的素数
- transient关键字
- eclipse 快捷键
- APicloud中右上角的小三角行的绘制
- 讲train_val.prototxt转化为deploy.prototxt
- Android源码中的静态工厂方法
- C++基础--内联函数
- suricata 3.1 源码分析21 (数据包处理1)
- 吴小龙的 mvp Android MVP 实例
- Linux 进程------sigaction 函数解析
- 第四周项目3-单链表的应用(3)
- Zookeeper常用小知识
- BDF快速入门笔记(一)
- Kubernetes1.4即将发布