Android的Message源码解读

实现了Parcelable ,可序列化的一个类。包含着要传递的信息。

1、常用的几个属性

int what消息的匹配code,用来区别不同的消息对应着不同的处理结果
int arg1/arg2Message携带的int信息,也可以通过setData设置
Object objMessage携带的Object信息,必须要经过Parcelable ,否则会抛出    java.lang.RuntimeException: Can't marshal non-Parcelable objects across processes.异常也可以通过setData设置
Handler target该Message对应的Handler,用来进行分发消息
Runnable callback处理消息,

2、常用的几个方法

(1)obtain()

从全局池中返回一个新的Message对象,避免重新分配一个新的对象。同时也可以根据不同的message参数创建出一个message对象来。其中这个全局池是一个链表。具体从全局池中怎么取出这个对象,可以参见下面的手绘图。

 private static final Object sPoolSync = new Object();
private static Message sPool; //当前节点对应的Message的信息 

 public static Message obtain() {
        synchronized (sPoolSync) {
                 //如果全局池不为空,则取当前最上面节点的,遵循先进后出原则
            if (sPool != null) {
                Message m = sPool;//当前message赋值给m,返回,并且next赋值为null
                sPool = m.next;//sPool变更为当前message的下一个节点,
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;//最后就相当于删除了最后插入的节点
                return m;
            }
        }
        return new Message();
    }

(2) recycle()

因为在回收的时候,是将当前message回收到全局池中。

message的全局池组成

 Message next; 
 private static final Object sPoolSync = new Object();
 private static Message sPool;
 private static int sPoolSize = 0;
 private static final int MAX_POOL_SIZE = 50;

其中线程的并发处理是通过synchronized (sPoolSync)对sPoolSync进行加锁。

为什么这里不采用对象锁或者类锁呢?

 由于静态方法,对象可能不存在,所以不可能用对象锁;

虽然类锁也可以实现,但是MessagePool逻辑和Message类本身关系不大,一方面不是很恰当,另一方面也限制了Message的拓展。

链表结构的实现

(aa) 加入链表

在MessageQueue移除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) {
//将当前的message插入到全局池
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

我们可以看到在加入到消息池的时候,首先需要将所有的字段都置空,避免Message过大,使静态线程池内存泄漏。所以这些缓存的对象所占的内存几乎可以忽略,并不会造成APP的OOM。

内置锁,只缓存50个,大于50的直接丢弃

分析下插入的过程,其实就是一个链表插入

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
//将当前的message插入到全局池
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }

当MessageQueue在removeMessage的时候,会执行到Message.recycleUnchecked()这个方法,然后就会按照左边图示的方式,将当前使用过的Message插入到链表中。最后生成的就是最后的链表结构。

(bb) 获取链表中的Message对象

另外在obtain()获取一个Message对象的时候,其实就是将全局池中的最上面的一个Message对象取出,可以参见上面上面手绘图的右半部分。

        synchronized (sPoolSync) {
                 //如果全局池不为空,则取当前最上面节点的,遵循先进后出原则
            if (sPool != null) {
                Message m = sPool;//当前最上面节点的message赋值给m,返回,并且next赋值为null
                sPool = m.next;//sPool变更为当前message的下一个节点,
                m.next = null;//并且next赋值为null
                m.flags = 0; // clear in-use flag
                sPoolSize--;//最后就相当于删除了最后插入的节点
                return m;
            }
        }
      

结合代码看下:

将最上面的节点取出赋值给返回的m对象,同时将sPool指向之前的上一个节点。注意返回的时候,还要将m上的next和flag都复位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值