Message object reuse (Flyweight mode in Android)

1. What is Flyweight Model

1 Introduction

Flyweight mode, namely FlyWeight, aims to reuse objects, avoid duplication and create a large number of objects, thereby saving system resource consumption

Flyweight mode introduction: https://www.cnblogs.com/adamjwh/p/9070107.html

Flyweight model practice: https://www.jianshu.com/p/b925b8cb6494

Second, the implementation in Message

We know that Android is an event-driven mechanism . There will be a combination of various events to form the display and interaction of the application, and it also involves the communication between threads. We all need to use Message to encapsulate an object through Message. , Which describes the type, parameters, and handlers of the event . It can be seen that with the continuous or concurrent events, Message will be created and generated by a large number of objects , and if a large number of objects are created frequently, it will cause serious consumption of memory , frequently trigger GC, etc., and affect application performance, so Use Flyweight mode to reuse the message object, it is not necessary to create a new object every time .

1. Object acquisition

There are two ways to obtain the Message object. One is to obtain a new object directly through the construction method new, and the other is to obtain an object using the obtain method. The official recommendation is to use obtain. Why, because it obtains an object from the object pool. Through the reuse mechanism to ensure that a large number of objects will not be generated; and new means that a new object is generated every time.

Let's look at the realization of 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();
}

After reading, there is probably such a logic, if sPool is not empty, then sPool is returned as a Message object, otherwise a new object is created, then what is this sPool? What is m.next? What are flags? What about sPoolSize?

Ok, with these questions, let's go to the global variables of Message to find:

// sometimes we store linked lists of these things
/*package*/ Message next;


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

private static final int MAX_POOL_SIZE = 50;

OK, we see that both sPool and next are of Message type. Think about it again, is it like a linked list? Yes, it actually maintains a linked list to store these recycled Message objects, and then retrieve them from the head of the linked list when used.

image

The general structure can refer to the above figure, sPool is similar to the role of a pointer, next points to the next message object that can be reused, if there is no available object, it points to null. Similarly, the initial value of sPool also points to a null reference. At this time, there is no object in the linked list, that is, it will enter the above code, return new Message();and directly create a new object and return.

We also see that there is a sPoolSize field and a MAX_POOL_SIZE field, which are easier to understand, one representing the length of the current linked list, and one representing the maximum length. So after obtaining an object in the above code, size–.

After getting the object, there is another setting flags = 0. Why is this? In fact, it's just a mark. Marking this object has already been used by someone. This mark will be used later, and it will be judged whether the object is being used during recycling.

2. Object preservation

As we saw above, there is such a linked list that stores and provides reused message objects. When are these objects inserted into the linked list? In the simple Flyweight mode, after we have created an object, we use the internal state as the key and the object as the value, which is saved in the map, but here is not saved when the object is created?

OK, continue to check and find the key position:recycle()

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

As you can see here, the mark just mentioned is used here. First, it will first determine whether it is available and can be recycled. If it is not, return directly or throw an exception, and enter the recycling program if it can be recycled.

/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 */
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;
    //类似于reset,将各个变量状态重置
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;
    
    //插入到链表表头
    //链表长度限制为50
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

This piece of code can be divided into two parts. The first part resets these attributes. After all, it is reused. If you don't give a brand new one, you have to give an empty one. You can't give it to an object with content. That will cause problems. The second part is to insert into the linked list. You can see that this is the operation of inserting into the head of the linked list. Give the message object pointed to by the current sPool to next, then point sPool to the current object, and finally size++.

sPool points to the head of the current linked list, this understanding is OK.

Get picture
Insert picture description here
Recycle
Insert picture description here

What about when is the recycle method called? When will it be recycled?
Hold down Ctrl on the method and found a bunch of places to call the method, but in fact, we just pay attention to a few places.

Message msg = mHandler.obtainMessage(MSG_DISABLE, state1, state2, animate);
        if (Looper.myLooper() == mHandler.getLooper()) {
            // If its the right looper execute immediately so hides can be handled quickly.
            mHandler.handleMessage(msg);
            msg.recycle();
        } else {
            msg.sendToTarget();
        }

Here you can see that after the handleMessage method is called, the recycle method is called.

if (Looper.myLooper() == mMainLooper) {
            mCallback.executeMessage(msg);
            msg.recycle();
            return;
        }

After the callback is executed, the recycle method is called.

3. Summary

Message is the Flyweight reuse. The Message class assumes multiple roles of Flyweight Abstraction, Flyweight Object, and Flyweight Factory Class, but it is actually quite easy to understand: a linked list is maintained internally to store up to 50 objects. When using obtain to obtain objects , Take out an object from the head of the table or create a new one. When the object is recycled, the properties of the object are reset and stored in the linked list.

Guess you like

Origin blog.csdn.net/lizebin_bin/article/details/91381542