android 享元模式,Android 享元模式

一、前言

享元模式即:Flyweight,它是对象池的一种实现。享元模式用来尽可能的减少内存的使用量。多用于存在大量重复对象的场景,或需要缓冲池的时候。用来缓存共享的对象。这样来避免内存移除等。

二、定义

运用共享技术有效的支持大量细粒度的对象。

三、例子

现在我们大概了解了什么是享元模式,概念再多不如一个简单例子来的痛快。下面我讲解一个查询巴士车票的例子来讲解什么是享元模式。

3.1、定义巴士车票

public interface Ticket {

public void showTicketInfo(String type);

}

public class BusTicket implements Ticket{

private static final String TAG = BusTicket.class.getSimpleName();

String from;

String to;

int price;

BusTicket(String from,String to) {

this.from = from;

this.to = to;

}

@Override

public void showTicketInfo(String type) {

price = new Random().nextInt(100);

Log.d(TAG,from + "到" + to + "的" + type + "巴士票价位" + price);

}

}

代码很简单根据from和to的地方来组成一张巴士车票。通过showTicketInfo可以打印车票价格信息。

3.2、工厂类辅助查询

我们先看这种写法:

public class TicketFactory {

public static Ticket getTicket(String from,String to) {

return new BusTicket(from,to);

}

}

比如当我们查询深圳到广州的巴士票的时候调用

Ticket ticket = TicketFactory.getTicket("shenzhen","guangzhou");

ticket.showTicketInfo("硬座");

看似没毛病,客户端这样调用查询就可以了。普通情况下这样确实没有问题,但当有大量客户同时查询的时候就可能会OOM了。因为我们会创建大量的相同对象,十分耗内存。当java的GC对这些对象回收的时候同样也属于资源浪费。这时候,该享元模式登场的时候了。我们只需要修改一下Factory。

public class TicketFactory {

static Map sTicketMap = new ConcurrentHashMap();

public static Ticket getTicket(String from,String to) {

String key = from + "-" + to;

if (sTicketMap.containsKey(key)) {

return sTicketMap.get(key);

} else {

Ticket ticket = new BusTicket(from,to);

sTicketMap.put(key,ticket);

return ticket;

}

}

}

这样用map来保存,以key查询,有重复就复用,没有就直接创建。避免了重复对象的创建。

四、JDK中的String

jdk中的String也是类似的消息池。一个String被定义过后就存在于常量池中。当其他地方使用相同的字符串时,实际使用的时缓存。

String str1 = new String("abc");

String str2 = new String("abc");

String str3 = "abc";

String str4 = "ab" + "c";

string一般我们用两个判断,

一个是equals,这个是比较内容,他们四个都相等。

另一个就是“==”判断。str1不等于str2,不等于str3。特别的,str3是等于str4。因为str1和str2是new的,str3是等号字面赋值。所以他们是不同的对象。而str4也是字面赋值。且str4使用了缓存在缓存池中的str3常量对象。所以用==判断的时候,他们是相同对象。

五、Message中的享元模式

关于Handler、Mesaage和Looper之间的关系这里不做详细讨论,有兴趣的同学可以看我另外一篇文章Android消息机制(Handler原理)-完全解析。这里我们只讲Message是如何使用享元模式。我们直接从发送message消息讲起,以下源码来自Android P。

5.1、handler发送message

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {

Message msg = Message.obtain();

msg.what = what;

return sendMessageAtTime(msg, uptimeMillis);

}

发送消息的时候最终调用sendEmptyMessageAtTime,通过Message.obtain();创建message并发送。享元模式就是从obtain这里切入。

5.2、Message.obtain

private static int sPoolSize = 0;

Message next;

private static final Object sPoolSync = new Object();

private static Message sPool;

public static Message obtain() {

synchronized (sPoolSync) {

if (sPool != null) {

Message m = sPool;

sPool = m.next;

m.next = null;

m.flags = 0; // clear in-use flag

sPoolSize--;

return m;

}

}

return new Message();

}

我们可以看到message要么是从最后new Message返回一个新的对象,要么返回sPool。当返回sPool时就是返回已创建的重复对象。再理解为啥sPool就是重复对象时,我们先看明白Message这个单链表对象。

5.3、Message是一个单链表对象

Message包含一个next的Message对象。sPoolSize表示个数,以此形成单链表结构。

单链表的取出

在obtain方法中

Message m = sPool ---m等于单链表

sPool = m.next ---sPool单链表舍弃表头元素

m.next = null; ---m舍弃除表头之外的所有元素

m.flags = 0; ---flag置0标记

sPoolSize--; ---单链表大小减1

这样就取出来了单链表的头元素并返回,而我们的单链表sPool舍弃表头。这样就完成了元素的复用。当然这里只是取出,接下来我们看插入

单链表的插入

插入的操作是在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;

synchronized (sPoolSync) {

if (sPoolSize < MAX_POOL_SIZE) {

next = sPool;

sPool = this;

sPoolSize++;

}

}

}

recycle判断Message对象是否正在被使用,如果是则刨除异常,否则开始进行recycleUnchecked单链表插入操作。插入之前先清空了各个参数。重点在最后

synchronized (sPoolSync) {

if (sPoolSize < MAX_POOL_SIZE) {

next = sPool;

sPool = this;

sPoolSize++;

}

}

if (sPoolSize < MAX_POOL_SIZE) ---单链表大小还没超过MAX_POOL_SIZE则开始插入

next = sPool; ---next直接等于自身

sPool = this; ---sPool等于现在插入的元素

sPoolSize++; ---大小+1

就是一个普通单链表插入表头的操作。

5.4、Message享元模式小结

虽然Message并不是最标准的享元模式用法,但它通过单链表这种方式一样实现了对象池的思想。也是另一种妙用。由此笔者想多说一句就是设计模式并不是一成不变的,理解它的核心,多思考你才能融会贯通。

六、写在最后

享元模式实现起来还是比较简单。在需要大量重复对象的时候起到重要作用。也用作缓存池等场景。能够降低内存中对象的数量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值