享元模式的使用和解析。

享元模式 常用方式及场景
常用方式 一般享元模式都是配合池化技术相互配合去使用,这种是为了节省内存空间,以及分配的时间
使用场景 有很多需要使用的复杂占用内存的对象,使用池化技术去缓存以节约分配时间以及内存
Android 使用方式 Handler 里面 Message 使用一个静态链表去共享这个 Message 池子的,当调用 Message.obtain() 方法去申请释放这样的话就可以复用他之前分配的各种东西了

Message 含有 13 个局部变量,这导致每次分配无论空间以及时间复杂度上都是比较可观的。

int what;
int arg1;
int arg2;
Object obj;
Messenger replayTo;
Long sendingUid;
Long workSourceUid;
int Flags;
Long when;
Bundle data;
Handler target;
Runnable callback;
Message next

代码:

/*
1. 抢到锁
2. sPool 不为空,头节点
3. 取到头节点然后 sPool 往后推一位,并且将取出来的和后面的链断开
4. 初始化 flags
5. 已经池化的 减一
6. 返回分配好的 Message
7. 池子里面没有,直接返回 new 出来的对象
*/
    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();
    }

/*
回收
1. 清空标志位以及自带各种的为初始值
2. 同步 sPoolSync 锁
3. 获取到锁之后判断小于 POOL 50 长度,设置自己的 next 为之前的 头节点,然后 sPool 更新头节点为当前已经清空所有状态的 Message
*/
    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 = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }  


/*
Message 是何时调用的 recycle 去回收到池子里面呢,咱们从 Handler 机制里面去探索下。

这里去添加的时候判断,如果这会正在推出,那么就会把这些已经分配好的去缓存到池子里面去,一般情况下我们不需要去主动 recycle 这些都是系统不同的使用地方帮咱们完成的。
*/

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
        // ...     
    }
}
// 另一处恢复 Message 的地方
Looper.loop() {
    // 每消费一个 Message 都会 recycle 一次,所以我们的池子里东西会很多滴。
    msg.recycleUnchecked();
}

/*
Hanlder 机制里面 消息的驱动流程为 epoll 阻塞去控制节奏,有几个触发点,一个是咱们 post 或者 sendMsg 的时候去 wakeup 另一个就是咱们 wakeUp 的时候判断 current time 小于 取出来的 msg.when 那么就会调用 native 去阻塞,另外就是 MessageQueue 里面第一时间去拿到 native 的 long 句柄。释放的时候是在 finalize 里面释放句柄的。
*/

Handler() {
    // 初始化的时候会先拿本身的 Looper 如果线程没有准备过 Looper 就会抛出异常。
    mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
    }
}

总结

享元模式翻译过来就是共享元素的意思,标准的享元模式里有共享以及不共享的内容,我们 Message 这里所有的都是可以共享的,享元模式一般就是用于节省空间及时间的分配,然后他们之间都有很多共有的元素。