Android中的享元模式

Posted by 夏敏的博客 on July 21, 2016

设计模式

前言

又来梳理知识点啦!

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

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

定义

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

使用场景

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

使用方法

享元模式是一种思想,一种避免多次重复创建对象的编程思想。
我们主要是要创建一个享元工厂,来生产我们的享元类。这样的享元工厂有N种写法,我们常见的是使用map来构造享元工厂,来看一个享元工厂的demo

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如下

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

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

    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指向下一个,同时把取到的置空。

    /*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

作者:Anderson大码渣,欢迎关注我的简书: Anderson大码渣