Android 메시지 메커니즘의 메시지 캐시 복구 메커니즘 분석

메시지

1.이 수업에 대한 공식 의견 :

/**
 * 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>
 */

번역:

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

둘째, 구성원 변수 분석 :

// 消息的唯一标识符,用来区分不同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;

셋, Message물건 가져 오기

우선, 매우 간단하고 아름다운 생성자를 봐야합니다.

여기에 사진 설명 삽입

Message객체를 얻기 위해 제공된 정적 메서드를 살펴 보겠습니다 .

여기에 사진 설명 삽입

객체 obtain를 가져 오는 방법 은 8 가지 Message가 있으며 이는 공식적으로 권장되는 방법이기도합니다. 먼저 추적을 용이하게하기 위해 번호를 지정하겠습니다.

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)

넷째, Message의 메시지 객체 풀과 매개 변수가없는 get () 메소드

공식적인 권장 사항은 obtain메서드 를 사용 하여 Message객체 를 얻는 것이므로이 메서드가 어떻게 생겼는지 살펴 보겠습니다.

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();
}

이 방법은 멤버 변수에 설계 sPool,, next, flags, sPoolSize이러한 변수 중 하나를 보면 첫 번째 이전 규칙을 의미한다 :

sPool: 그것은 Message객체입니다. 이름 만보고 "pool", 메시지 객체 풀로 이해합니다. 실제로 재사용 된 메시지에 대한 헤드 포인터입니다 (아래 그림 설명이 있습니다).

next: 그것은 Message객체입니다. 이름을보고 "next"로 이해합니다. 실제로 next는 두 Message객체 연결하는 꼬리 포인터입니다 .

flags: 세 종류의 메시지 태그;

sPoolSize: 메시지 개체 풀의 크기입니다.

좋습니다. 변수가 해석 된 후이 메서드가 수행하는 작업을 살펴 ​​보겠습니다. 먼저 sPool비어 있는지 확인하고 비어 있으면 직접 반환 합니다 new Message(). Nima는 new Message ()에 대한 공식 권장 사항이 아닙니다. 공식적으로 권장하지 않습니다. 따라서 대부분의 경우 sPool비어 있지 않습니다.이 변수가 할당 된시기를 알아 보겠습니다.

여기에 사진 설명 삽입

sPool할당 할 수있는 장소는 두 군데가 있는데 , 하나는 위의 obtain()방법이고 다른 하나는 recycleUnchecked()방법입니다. 이것은 다루기 쉽습니다.이 메서드의 구현을 살펴 보겠습니다.

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++;
        }
    }
}

이 방법은 메시지를 재 확보하고 재 확보 된 메시지를 메시지 오브젝트 풀에 넣는 것입니다. Next obtain()recycleUnchecked()메서드는 함께 결합하여 메시지 개체 풀을 탐색합니다.

3. 메시지 개체 풀

메시지 개체 풀과이 것이 메시지를 수집하는 방법을 종합 obtain()하고 recycleUnchecked()이해합니다.

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();
}

좋아, 태그 순서를 재생하십시오. 다시 이동 obtain()하고 recycleUnchecked()코드 블록을 동기화해야합니다.

메시지 개체 풀이 new Message()처음에 비어 있다고 가정하면 처음부터이 메시지가 제거되어 재활용 할 준비가됩니다. →recycleUnchecked()

next = sPool, 시작 메시지 객체 풀이 비어 있음, sPool비어 있음, next비어 있음; 여기서는 sPool객체에 할당 된 in-use객체로 이동합니다. next
sPool = this, this현재 Message하나가 메시지 객체와 함께 대상 셀로 다중화 되었음을 나타냅니다 .
sPoolSize++, 기본값은 0입니다. , 이때 메시지를 입력하십시오. +1.

이해하기 위해 그림을 그려 봅시다. 전의 첫 번째 Message개체 가라고 가정 하고 개체 풀에 들어가야 m_1하는 메시지 ( m_2) 가 있으면 다시 호출합니다 recycleUnchecked().
여기에 사진 설명 삽입

프로세스를 다시 단순화합니다. 실제로 메시지 개체 풀에 메시지가 있고 sPool포인터가 왼쪽과 오른쪽으로 이동합니다.

여기에 사진 설명 삽입

이러한 방식으로 메시지 복구, 즉 링크드리스트 복구가 가능하고 스레드 동기화가 보장되므로 항상 sPoolSize == 50최대 값 까지 체인 구조가 형성된다 .

OKK, Message객체 가 위에서 복구 되었고 우리가 obtain()하나를 얻었다 고 가정 할 때 , 우리는 message어떤 종류의 프로세스를 따를 것입니까?

Ⅰ. 헤드 포인터 sPool가 비어 있지 않은지 판단합니다. 분명히 sPool더 이상 비어 있지 않습니다.

Ⅱ. Message m = sPool;,개체 풀에서 Message개체 를 꺼내서 할당합니다 m.

Ⅲ. sPool = m.next;메시지 객체 풀의 다음 재사용 가능한 객체 (m.next)를 sPool( 객체 풀 의 현재 Message 객체, 이전 풀에있는 경우 이번에) 할당합니다 sPool == null. 풀에서 가져옴 개체가 재사용되는 경우 체인에서 연결을 끊으면 헤드 포인터가 항상 다음 재사용 가능한 개체를 가리켜 야합니다.

Ⅳ. m.next = null;두 메시지 사이의 연결 끊으 려면 평신도의 용어로 두 메시지를 연결하는 "다음"을 잘라서 더 이상 관련되지 않도록합니다.

Ⅴ. m.flag = 0, "사용중"표시;

Ⅵ. sPoolSize--;메시지를 꺼내면 확실히 단위의 길이를 줄일 수 있습니다.

이해하기 위해 그림을 그리는 오래된 규칙 :

여기에 사진 설명 삽입

이 그림은 이해하기 쉬워야하며, 그 in-use물건은 다 recycleUnchecked()사용한 후에 재활용 될 것 입니다. 그래서 이것을 어디 recycleUnchecked()라고 부릅니까? Message소스 코드를 열고 recycle()메서드를 호출 합니다 .
여기에 사진 설명 삽입

이 시점 에서 obtain()recycleUnchecked()방법에 대한 해석이 완료되었습니다.

다섯, 매개 변수가있는 다른 7 가지 방법을 얻습니다.

1. 방법 기능 :

public static Message obtain(Message orig): 메시지 개체 풀에서 메시지 개체 m을 가져 와서 orig의 모든 속성을 m에 할당합니다.

public static Message obtain(Handler h): 메시지 개체 풀에서 메시지 개체 m을 가져 와서 m의 대상을 다시 할당합니다.

public static Message obtain(Handler h, Runnable callback): 메시지 객체 풀에서 메시지 객체 m을 얻은 다음, m의 대상과 m의 콜백을 재 할당합니다.

public static Message obtain(Handler h, int what): 메시지 개체 풀에서 메시지 개체 m을 가져 와서 m의 대상과 m의 대상을 재 할당합니다.

public static Message obtain(Handler h, int what, Object obj): 메시지 개체 풀에서 메시지 개체 m을 가져 와서 m의 target, what 및 obj의 세 멤버 변수를 재 할당합니다.

public static Message obtain(Handler h, int what, int arg1, int arg2): 메시지 객체 풀에서 메시지 객체 m을 얻은 다음, m의 target, what, arg1, arg2의 네 가지 멤버 변수를 재 할당합니다.

public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj): 메시지 개체 풀에서 메시지 개체 m을 가져 와서 m의 target, what, arg1, arg2, obj의 5 개 멤버 변수를 재 할당합니다.

2. 요약 :

매개 변수화 된 obtain메소드 첫 번째 행은 객체 obtain()얻기 위해 먼저 호출하는 Message것이지만 매개 변수화 된 오픈 소스는 매개 변수를 전달하여 일부 멤버 변수를 재설정합니다.

Six, Message의 캐시 복구 메커니즘 -Flyweight 모드

AndroidMessage회사의 모든 메시지 메커니즘 은이 캐리어를 통해 메시지 전송하는데 매번 "new"를 통해 객체를 얻으면 필연적으로 메모리 사용량이 많아지고 성능이 저하됩니다. 소스 코드 연구를 통해 Message캐시 복구 메커니즘을 이해 했습니다. 사실 이것은 Flyweight 모델 의 구현입니다.

추천

출처blog.csdn.net/C_biubiubiu/article/details/113558765