Android的Message源码解读

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

1、常用的几个属性

int what 消息的匹配code,用来区别不同的消息对应着不同的处理结果
int arg1/arg2 Message携带的int信息,也可以通过setData设置
Object obj Message携带的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都复位。

猜你喜欢

转载自blog.csdn.net/nihaomabmt/article/details/83340998