Android之Message源码分析(第二篇:消息池)

前言

    Message位于android/os/Message.java文件中 

两个向消息池中添加(回收)一个Message对象的2个方法:recycle()和recycleUnchecked()方法

 

从消息池获取一个Message对象的方法:obtain()方法

今天一起学习Message对象的对象池设计…………

recycle()方法分析

    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

    用于向消息池添加(回收)Message对象的方法,从消息池(Message对象池)的角度来看,就是回收一个Message对象,所以此方法命名为recycle非常贴切。

在方法的内部会根据当前Message对象的状态做出处理,正在消息池中的Message对象(可用状态)与未在消息池中的Message对象(不可用状态),当前Message对象处于可用状态时,才会进一步调用recycleUnchecked()方法进行Message对象的回收

第一:可用状态:首先执行了isInUse()方法,该方法返回当前Message对象的是否为可用状态,isInUse()方法返回的是当前Message对象可用的情况下,说明Message对象在消息池中可使用,且gCheckRecycle标志位是开启状态(API版本大于等于21是开启状态)的话,就直接抛出IllegalStateException对象,用于告知调用者,Message对象是可用状态,不能再次缓存,因为它正在消息池中,当gCheckRecycle标志位未开启(API版本小于21是未开启状态),方法会执行到return语句,整个方法结束,当前Message对象也不会被缓存

第二:不可用状态:当前Message对象为不可用状态,说明已经从消息池中取出来使用过了,则会进行到recycleUnchecked()方法,该方法会实际执行缓存Message对象

isInUse()方法分析

boolean isInUse() {
        return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
    }

返回Message对象的可用状态,true表示Message对象为可用状态(即正在消息池中),false表示Message对象为不可用状态(已经从消息池中取出来使用了),举个例子

00000001 flags

00000001 FLAG_IN_USE

按位与结果为

00000001

说明当前Message对象为可用状态,即Message对象正在消息池中,等待取出

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++;
            }
        }
    }

这是真正回收Message对象(缓存Message对象)的地方,要把一个对象初始化为缓存对象,要做两件事情

第一:为当前Message对象持有的标记flags赋值为可用状态,说明可以在消息池中被去取出来使用,继续为Message对象中持有的实例变量what赋值为0,arg1为0,arg2为2,ojb指向null,replyTo指向null,sendingUid赋值为-1,when赋值为0,target赋值为null,callback赋值为null,data赋值null,可见当前Message对象持有的属性全部赋值为零值,就是当下的第一件事,这样当前的Message对象就和通过new创建一个新的Message对象的状态是一样的!

第二:已经初始化为零值状态的当前Message对象会被添加到一个单链表中作为头结点,在缓存过程中,当前执行缓存任务的Thread对象需要先持有sPoolSync对象对应的锁,这是为了确保三个共享变量next、sPool、sPoolSize在多线程下均可以被正确写入,若同一时刻有多个线程写入三个共享变量,则三个共享变量的值会被线程间相互覆盖掉,判断当前已经缓存的Message对象数量是否小于最大的缓存对象总数50,小于获得指向第一个Message对象的sPool变量,sPool变量是单链表的头结点引用,它总是指向单链表中的第一个Message对象,然后sPool先将自己目前持有的头结点Message对象赋值给当前Message对象的next实例变量负责持有上,接着sPool指向当前Message对象,最后再将消息池的总数+1,就完成了当前Message对象的缓存工作

消息池相关的字段介绍(Message类持有的与消息池相关的类变量与常量)

    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50; 消息池存储的Message对象最大数量

    private static boolean gCheckRecycle = true; 标记当前是否需要检查Message对象正在使用

Message类持有的Object对象oPoolSync(常量),作为线程间同步使用的对象锁(注:sPoolSync的访问权限是public权限(对我们是hide))

Message类持有的Message对象sPool,永远指向的是单链表的第一个Message对象(头结点对象)(实现缓存Message对象的消息池,使用的是单链表),因为sPool是类变量,它位于方法区,与class的生命周期一样,为此会阻止GC回收单链表中的每一个对象,以达到内存缓存的目的

Message类持有的int变量sPoolSize,代表消息池(单链表)中缓冲的Message对象的数量,即实际缓存结构单链表的元素总数

Message类持有的常量MAX_POOL_SIZE,代表的是消息池最大可缓存的Message对象的数量,这里是50

Message类持有的标志位gCheckRecycle,代表是否需要检查Message对象的可用状态

FLAG_IN_USE常量介绍

    /*package*/ static final int FLAG_IN_USE = 1 << 0;

用于标记Message对象的状态,1左移0位,仍是二进制……省略000000001

从消息池的角度来看,这个状态表示两个意思

可用状态:表示Message对象正在消息池中,可取出使用(说明不可回收)

不可用状态:表示Message对象已经从消息池中,被取出来使用(说明可回收)

updateCheckRecycle()方法分析

    /** @hide */
    public static void updateCheckRecycle(int targetSdkVersion) {
        if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
            gCheckRecycle = false;
        }
    }

hide权限的方法,用于更新gCheckRecycle标志位,在目标SDK小于API21时会更新为false,gCheckRecycle标记是否需要检查Message对象使用状态的标志位,目前不知道在哪里调用

public static final int LOLLIPOP = 21;

obtain()方法分析

    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对象的方法,原来它是从Message类持有的消息池sPool中取出一个Message对象供我们使用的,这是google推荐使用该方法的原因!详细分析见第一篇文章

总结

1、一个用户进程中,会使用无数个Message对象,将使用过的Message对象在内存中缓存起来,后面复用时只需改变对象持有的属性(实例变量)即可,复用对象可以节约内存空间,也可以节约创建对象的性能开销,还可以减少GC的工作,这是官方推荐我们使用obtain()系列方法获取Message对象的真正原因,牛B!

2、Message类持有作为消息池头引用的静态变量sPool,消息池是以单链表结构进行实现的,sPool是一个静态变量,它引用的对象的生命周期与类的生命周期一样长,这样处于单链表中的每一个Message对象均不会被gc回收,所以它们可作为内存缓存使用

3、消息池(单链表)最大可以缓存50个Message对象,也就是说单链表的最大长度为50个元素

4、当缓存的Message对象总数已经为50个时,不能再被缓存的Message对象会被gc回收掉

5、保留在消息池(单链表)中的Message对象,都被标记成了FLAG_IN_USE,表示Message对象是可用状态

6、在消息池中缓存Message对象、获取Message对象,均是在头结点处完成的,传说中的头插法,即在头部插入元素、也在头部取出元素,这是单链表最高效的插入与删除了,时间复杂度O(1),运用的牛逼!

7、缓存对象时,将Message对象的实例变量均赋值为零值(初始值),牛BBBBB

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值