Android最简单易懂的消息机制(Message MessageQueue Handler Looper)(2)

在上篇文章 Android最简单易懂的消息机制(Message MessageQueue Handler Looper)(1) 中,我们已经聊过了Looper MessageQueue 与 Handler,现在就来一起看看消息机制中传递的载体Message吧~


Message

首先来看一看官网中对Message的介绍及属性描述:

Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.

定义了一个包含描述和任意数据对象的Message,可以发送给Handler。这个对象含有两个额外的int字段和一个额外的对象字段,且允许你在很多情况下不执行分配。

public intarg1

arg1 and arg2 are lower-cost alternatives to using setData() if you only need to store a few integer values.

//如果你只需要储存简单的int类型数据,可以使用arg1和arg2来代替使用setData()传入一个Bundle参数

public intarg2

arg1 and arg2 are lower-cost alternatives to using setData() if you only need to store a few integer values.

//如果你只需要储存简单的int类型数据,可以使用arg1和arg2来代替使用setData()传入一个Bundle参数

public Objectobj

An arbitrary object to send to the recipient.

//发送给接收者的一个任意对象

public MessengerreplyTo

Optional Messenger where replies to this message can be sent.

//在IPC中使用,指定回复的Messenger

public intsendingUid

Optional field indicating the uid that sent the message.

//指示发送消息的uid,仅适用于Messenger发送的消息,否则就是-1

public intwhat

User-defined message code so that the recipient can identify what this message is about.

//用户定义的消息代码,以便接收方能够识别此消息是关于什么的。每个Handler都有自己的消息代码名称空间,所以您不必担心Handler之间标识冲突。

/*package*/ int flags;
//message的标志,比如当这个消息被处理时,会将其Flag标志为FLAG_IN_USE,防止该消息再入队,并将其添加到了回收消息的链中

/*package*/ long when;
//message入队时间,比如也许会延时

/*package*/ Bundle data;
//message中携带的数据

/*package*/ Handler target;
//与message相关联的目标handler

/*package*/ Runnable callback;
//若通过handler.post(runnable r)方法,则runnable会转成message,callback就是runnable中的回调方法

其实Message作为消息机制的载体,主要就是作为一个实体对象携带一些属性进入到轮循中,所以没有什么逻辑,很好理解,主要就来看一下Message的获取与回收复用吧。

虽然说Message允许我们直接使用其构造方法创建,但是官方还是推荐使用Message.obtain():

Message()

Constructor (but the preferred way to get a Message is to call Message.obtain()).

 Message.obtain() 有8个重载方法,实质上都是获取一个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;
    }

很好理解,属性的意义上面也讲过了,让我们直接看一下最关键的obtain()方法:

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    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();
    }

可以看到,如果 sPool 存在就从复用消息链表头部取一个消息,然后重置它的标志位和连接下一个message的变量,如果不存在就新建一个消息。

这样说大家也许就有些疑惑了,这就是Message的回收与复用了,接下来就让我们首先弄明白什么是sPool next sPoolSize吧。

    // sometimes we store linked lists of these things
    //在消息回收机制中,连接下一个message的成员变量
    /*package*/ 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;

首先什么是消息池呢,引用我之前在某篇博客中看到的(真忘了是哪位大神了,之前是存在了云笔记中= =):

Message对象池其实是通过链表的结构组合起来的池。上面有三个message,分别为message1、message2、message3,他们的连接关系分别通过其成员变量next进行衔接,如:message1.next=message2;message2.next=message3,现在我们就明白了message.next()的作用了,那么什么是sPool呢:

作为一个私有的静态变量,sPool就是指向消息池里的第一个对象,也就是表头,即如上message1是表头的第一个元素,而sPool指向表头,也就是指向message1。

 

现在我们再来看一下obtain() 方法,首先我们会判断sPool是否为空,即如果它为空,说明消息池就为空,代表没有复用的Message,这时只能新建一个Message。而如果sPool不为空,那么我们就会将sPool指向的message1赋值给当前要创建的Message m,同时将message1的next即message2赋值给sPool,而新建出的Message m就将其的next清空,重置标记位,代表着新建的m可以进行入队操作,同时消息池中所含可复用消息的数量sPoolSize也就相应减少。

看到这里,大家是不是对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中的所有状态还原,同时将flag标志为 FLAG_IN_USE,防止再入队。

而由于每次回收的Message都会插入到表头,所以将当前表头的sPool赋值给此message的next,再将此message赋值给sPool,同时消息池中所含可复用消息的数量sPoolSize也相应增加。

那么recycleUnchecked()什么时候会被调用呢,其实在MessageQueue 和 Looper 中都有,在MessageQueue.removeMessages()和Looper.loop()的方法中都会执行,将message清空,赋上 FLAG_IN_USE 的标记,回收到了可复用的消息池中。

那么至此,我们对于Message的缓存回收机制应该彻底明白了吧。


吼吼~感谢大家耐着性子看到这里,对于Android消息机制,咱们都是小有所成啦~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值