Android中的享元模式

设计模式

前言

又来梳理知识点啦!

在有的时候我们要多次使用某个类中的公有实例方法,我们通常的做法是,先new一个该类的实例,然后再调用该类的这个方法,调用完毕后这个类也就变成垃圾。这种调用方式如果出现的频率很高,会在对象生成和内存占用上浪费很多的资源,一个对象的创建和销毁是很占资源的。

于是,伟大的程序员们想到一个好办法,尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。经典的享元模式中,是使用一个map来存储对象,相当于是一个对象工厂,客户端每次都从享元对象工厂中获取对象。

定义

使用共享对象可有效的支持大量的细粒度对象

使用场景

  1. 系统中存在大量的相似对象
  2. 对象没有特定的身份,状态都较接近
  3. 需要缓冲池的场景

使用方法

享元模式是一种思想,一种避免多次重复创建对象的编程思想。

我们主要是要创建一个享元工厂,来生产我们的享元类。这样的享元工厂有N种写法,我们常见的是使用map来构造享元工厂,来看一个享元工厂的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.HashMap;
public class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap();
//使用传统的map来管理对象
public static Shape getObj(String color) {
Circle circle = (Circle)circleMap.get(color);
if(circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color : " + color);
}
return circle;
}
}

Android中的享元模式应用

应用的话,若我说,我们编写的ViewHolder都可以归类为享元工厂的存储器。几乎到处是应用,不过最明显的是Handler中Message的应用。

例如我们发送个0给handler自己。

handler.obtainMessage(0).sendToTarget();

我们进去看Handler的源码,obtainMessage如下

1
2
3
4
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}

我们发现,其中调用了Message.obtain,我们再去看Message的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
//获取一个空message
public static Message obtain() {
synchronized (sPoolSync) {
//从对象Message中获取message
if (sPool != null) { //private static Message sPool;
Message m = sPool;
sPool = m.next;
//清空message属性
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

我们可以看到,sPool指向的是一个链表,其实这个链表是存储我们用过的Message的,当我们obtain一个Message的时候,会去取链表中的第一个,并把sPool指向下一个,同时把取到的置空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*Message的回收方法*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
/*将用过的Message放入链表中*/
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}

我们可以知道Message的享元模式不是使用的传统的map方式,而是自己构建一个链表,灵活使用我们的享元模式思想才是重点。

到了最后,我们可以思考一下,ListView中每个View的创建是不是也用了享元模式呢?


谢谢大家阅读,如有帮助,来个喜欢或者关注吧!


本文作者:Anderson/Jerey_Jobs

博客地址 : 夏敏的博客/Anderson大码渣/Jerey_Jobs

简书地址 : Anderson大码渣

CSDN地址 : Jerey_Jobs的专栏

github地址 : Jerey_Jobs