Analysis of Message Cache Recovery Mechanism of Android Message Mechanism

Message

1. The official comment on this class:

/**
 * Defines a message containing a description and arbitrary data object that can be
 * sent to a {@link Handler}.  This object contains two extra int fields and an
 * extra object field that allow you to not do allocations in many cases.
 *
 * <p class="note">While the constructor of Message is public, the best way to get
 * one of these is to call {@link #obtain Message.obtain()} or one of the
 * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
 * them from a pool of recycled objects.</p>
 */

translation:

定义一条消息,其中包含可以发送给Handler的描述和任意数据对象。 该对象包含两个额外的int字段和一个额外的对象字段,使您在许多情况下不进行分配(避免重复使用)。
虽然Message的构造函数是公共的,但获取其中之一的最佳方法是调用Message.obtain()或Handler.obtainMessage()方法之一,这将从回收对象池中拉出它们。

Two, member variable analysis:

// 消息的唯一标识符,用来区分不同Handler的相同消息(防止混淆);一般用16进制来表示。
public int what;

// Message类的可选变量,当我们只需要放简单的整型值时就可以直接赋给这俩个变量而不用去setData或设置obj。
public int arg1;
public int arg2;

// Message携带的任意数据类型的对象,并且这个对象包含Parcelable类的时候,它必须是非空的。对于其他数据的传输,建议使用setData()方法
public Object obj;

    // 回复跨进程的Messenger 
    public Messenger replyTo;

    // Messager发送这的Uid
    public int sendingUid = -1;

    // 正在使用的标志值 表示当前Message 正处于使用状态,当Message处于消息队列中、处于消息池中或者Handler正在处理Message的时候,它就处于使用状态。
    /*package*/ static final int FLAG_IN_USE = 1 << 0;

    // 异步标志值 表示当前Message是异步的。
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

    // 消息标志值 在调用copyFrom()方法时,该常量将会被设置,其值其实和FLAG_IN_USE一样
    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

    // 消息标志,上面三个常量 FLAG 用在这里
    /*package*/ int flags;

    // 用于存储发送消息的时间点,以毫秒为单位
    /*package*/ long when;

    // 用于存储比较复杂的数据
    /*package*/ Bundle data;
    
    // 用于存储发送当前Message的Handler对象,前面提到过Handler其实和Message相互持有引用的
    /*package*/ Handler target;
    
    // 用于存储将会执行的Runnable对象,前面提到过除了handlerMessage(Message msg)方法,你也可以使用Runnable执行操作,要注意的是这种方法并不会创建新的线程。
    /*package*/ Runnable callback;
 
    // 指向下一个Message,也就是线程池其实是一个链表结构
    /*package*/ Message next;

    // 该静态变量仅仅是为了给同步块提供一个锁而已
    private static final Object sPoolSync = new Object();

    //该静态的Message是整个线程池链表的头部,通过它才能够逐个取出对象池的Message
    private static Message sPool;

    // 该静态变量用于记录对象池中的Message的数量,也就是链表的长度
    private static int sPoolSize = 0;
  
    // 设置了对象池中的Message的最大数量,也就是链表的最大长度
    private static final int MAX_POOL_SIZE = 50;

     //该版本系统是否支持回收标志位
    private static boolean gCheckRecycle = true;

Three, get the Messageobject

First of all, I must look at the constructor, which is very simple and beautiful:

Insert picture description here

Let's look at the provided static Messagemethod of obtaining objects:

Insert picture description here

There are 8 obtainways to get the Messageobject, which is also the official recommended method; let's first give a number to facilitate tracking:

public static Message obtain()public static Message obtain(Message orig)public static Message obtain(Handler h)public static Message obtain(Handler h, Runnable callback)public static Message obtain(Handler h, int what)public static Message obtain(Handler h, int what, Object obj)public static Message obtain(Handler h, int what, int arg1, int arg2)public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)

Fourth, the message object pool of Message and the obtain() method without parameters

Since the official recommendation is to use obtainmethods to obtain Messageobjects, let's take a look at how this method looks like:

1.obtain():

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 *
 * 翻译:从消息对象池中返回一个新的Message实例,这样可以避免重复创建冗余的对象
 */
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();
}

This method is designed to member variables sPool, , next, flags, sPoolSizemeaning the old rules the first to look at one of these variables:

sPool: It is an Messageobject; just look at the name, understand it as "pool", the message object pool; in fact, it is the head pointer to the reused message (there is a picture description below);

next: It is an Messageobject; just look at the name and understand it as "next", which is actually the tail pointer, which nextconnects the two Messageobjects;

flags: Three kinds of message tags;

sPoolSize: The size of the message object pool.

Ok, after the variables are interpreted, let’s take a look at what this method does: first determine sPoolif it’s empty, and new Message()return directly if it’s empty . Nima, isn’t the official recommendation for new Message(), think carefully, since the official does not recommend it, So in most cases, it sPoolis not empty, let's find out when this variable is assigned:

Insert picture description here

It was found that there are two places to sPoolassign values, one is the above obtain()method, and the other is the recycleUnchecked()method. This is easy to handle, let's take a look at the implementation of this method.

2.recycleUnchecked():

/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 * 
 * 翻译:回收可能正在使用的消息。 在处理排队的消息时,由MessageQueue和Looper内部使用。
 */
@UnsupportedAppUsage
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++;
        }
    }
}

This method is to reclaim the message and put the reclaimed message in the message object pool. Next obtain()and recycleUnchecked()methods combine together to explore the message object pool.

3. Message object pool

Synthesize obtain()and recycleUnchecked()understand how the message object pool and this thing collect messages:

void recycleUnchecked() {
    
    
                ...
        if (sPoolSize < MAX_POOL_SIZE) {
    
    
                // ⑴
                next = sPool;
                 // ⑵
                sPool = this;
                 // ⑶
                sPoolSize++;
                 ...
         }
    }

public static Message obtain() {
    
    
    synchronized (sPoolSync) {
    
    
        // Ⅰ
        if (sPool != null) {
    
    
            // Ⅱ
            Message m = sPool;
            // Ⅲ
            sPool = m.next;
            // Ⅳ
            m.next = null;
            // Ⅴ
            m.flags = 0; 
            // Ⅵ
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

ok, play tag order, we have to go again obtain()and recycleUnchecked()synchronization code block:

Assuming that the message object pool is empty at the new Message()beginning, then from the beginning, this message will be taken out and ready to be recycled →recycleUnchecked()

next = sPool, a start message object pool is empty, it sPoolis empty, that nextempty; herein is sPooldirected to the object assigned to in-usethe object next
sPool = this, thisrepresents the current Messageone is multiplexed as a target cell with the message object;
sPoolSize++, The default is 0, at this time enter the message, +1.

Let's draw a picture to understand. Assuming that the first Messageobject before is assumed to be m_1, if there is a message ( m_2) that needs to enter the object pool, call again recycleUnchecked():
Insert picture description here

Simplify the process again. In fact, there is a message into the message object pool, and the sPoolpointer moves left and right:

Insert picture description here

In this way, the message can be recovered, that is, the recovery of the linked list, and thread synchronization is guaranteed, so a chain structure will always be formed until the sPoolSize == 50maximum value.

OKK, assuming that an Messageobject has been recovered above and we obtain()obtain one message, what kind of process will we follow?

Ⅰ. Judge whether the head pointer sPoolis not empty, obviously it sPoolis not empty anymore;

Ⅱ. Message m = sPool;,Take out an Messageobject from the object pool and assign it to it m,

Ⅲ. sPool = m.next;Assign the next reusable object (m.next) of the message object pool to sPool(the current Message object in the object pool, if there was one in the previous pool, then this time sPool == null); in layman 's terms, it is taken from the pool If an object is reused, disconnect it from the chain, then the head pointer must always point to the next reusable object;

Ⅳ. To m.next = null;disconnect the link between the two messages, in layman's terms, cut the "next" linking the two messages so that they are no longer involved;

Ⅴ. m.flag = 0, put the "in use" mark;

Ⅵ. If sPoolSize--;you take out a message, it will definitely reduce the length of a unit.

Old rules, drawing to understand:

Insert picture description here

This picture should be easy to understand, then the in-useobject will be recycleUnchecked()recycled after it is used up . So where is this recycleUnchecked()called? Open the Messagesource code and recycle()call in the method:
Insert picture description here

At this point, the interpretation of the obtain()and recycleUnchecked()method is complete.

Five, obtain the other 7 methods with parameters

1. Method function:

public static Message obtain(Message orig): Obtain a Message object m from the message object pool, and then assign all the attributes in orig to m;

public static Message obtain(Handler h): Obtain a Message object m from the message object pool, and then re-assign the target of m;

public static Message obtain(Handler h, Runnable callback): Obtain a Message object m from the message object pool, and then reassign the target of m and the callback of m;

public static Message obtain(Handler h, int what): Obtain a Message object m from the message object pool, and then reassign the target of m and the what of m;

public static Message obtain(Handler h, int what, Object obj): Obtain a Message object m from the message object pool, and then reassign the three member variables of m's target, what and obj;

public static Message obtain(Handler h, int what, int arg1, int arg2): Obtain a Message object m from the message object pool, and then reassign the four member variables of m's target, what, arg1, and arg2;

public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj): Obtain a Message object m from the message object pool, and then reassign the five member variables of m's target, what, arg1, arg2, and obj.

2. Summary:

The obtainfirst line of the parameterized method is to first call to obtain()obtain an Messageobject, but the parameterized open source resets some member variables by passing in parameters.

Six, Message's cache recovery mechanism-Flyweight mode

AndroidAll of the message mechanism Messageof the company transmits messages through this carrier. If we obtain objects through "new" every time, it will inevitably cause high memory usage and reduce performance. Through the study of its source code, I understand Messagethe cache recovery mechanism, in fact, this is the embodiment of the Flyweight model .

Guess you like

Origin blog.csdn.net/C_biubiubiu/article/details/113558765