0
点赞
收藏
分享

微信扫一扫

Android 消息机制之 Message 与消息对象池的深入源码分析 [ 四 ]

Android 消息机制深入源码分析 [ 一 ]
Android 消息机制之 ThreadLocal 深入源码分析 [ 二 ]
Android 消息机制之 Looper 深入源码分析 [ 三 ]
Android 消息机制之 Message 与消息对象池的深入源码分析 [ 四 ]
Android 消息机制之 MessageQueue 深入源码分析 [ 五 ]
Android 消息机制之初识Handler [ 六 ]
Android 消息机制之 Handler 发送消息的深入源码分析 [ 七 ]
Android 消息机制之 MessageQueue.next() 消息取出的深入源码分析 [ 八 ]
Android 消息机制之消息的其他处理深入源码分析 [ 九 ]
Android 消息机制总结 [ 十 ]

接上一章的 Looper, 本章开始分析消息机制中的 Message 与消息对象池.

什么是 Message

  • 一个可以发送给 Handler 的描述和任意数据对象的消息对象, 这个对象包含两个额外的 int 字段和一个额外的对象字段. 这样就可以使用在很多情况下不用做分配工作.
  • 尽管 Message 的构造函数是公开的, 但是获取 Message 的对象最好的方式是调用 Message.obtain() 或者 Handler.obtainMessage(), 因为这样是直接从一个可回收的消息对象池中获取 Message 对象.

成员变量 what

  • 官方: 它是用户定义 Message 的标识符, 用以分辨消息的内容, Handler 拥有自己消息代码的命名空间, 因此你不用担心与其他的 Handler 冲突.

成员变量 arg1 与 arg2

  • arg1 与 arg2 都是 Message 的可选变量, 可以用来存放两个整数值, 不用访问 obj 对象就能读取的变量. 如果只是几个整形的数值, 相对于使用 setData() 方法, 使用 arg1/arg2 是较低成本的替代方案.

成员变量 obj

  • 官方: 将一个独立的对象发给接收者. 当使用 Messenger 去发送消息, 并且这个对象包含 Parcelable 类的时候, 它必须是非空的, 对于其他的数据传输, 建议使用 setData() 方法.

剩余成员变量

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

//Messenger 发送的 UID
public int sendingUid = -1;

//正在使用的标志值,表示当前 Messgae 正处于使用状态,
//当 Message 处于消息队列中,处于消息池中或者 Handler 正在处理 Messgae 的时候,它就处于使用状态
/*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;

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

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

//用于存储比较复杂的数据
/*package*/ Bundle data;

//用于存储发送当前 Message 的 Hadnler 对象, 说明 Handler 其实和 Message 是相互持有引用的.
/*package*/ Handler target;

//用于存储将会执行的 Runnable 对象,
//除了 HandlerMessgae(Message msg) 方法,还可以使用 Runnable 执行操作.要注意的是这种方法并不会创建新的线程
/*package*/ Runnable callback;

//指向下一个 Messgae, 
/*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;

1. Message.obtain

在 Message.java 中, 通过 Message.obtain() 方式来获取 Message 对象的, 一共有 8 个方法.下面将会针对这些逐个分析一下.

1.1 public static Message obtain( )

代码位于 Message.java 126 行

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, 那么 sPool 到底有什么用呢? 这就涉及到了 Message 的设计原理了. 这里就引出了 Message 中的消息对象池了.

消息对象池

private static Message sPool;

乍一看, 好像没什么, 就是一个 Message 对象而已, sPool 对象默认是 null.
这时候回头来看上面的无参 obtain 方法, 内部直接 new Message() 返回了. 但是官方又不推荐直接 new Message(), 所以推断 sPool 在大部分情况下, 是不会为 null 的. 那么就找一下, 看在什么地方会对 sPool 进行赋值.
经过搜索后发现, 整个 Message 就有两次地方对 sPool 进行赋值.

  1. 上面的无参 obtain 方法中.
  2. void recycleUnchecked() 方法中.

首先第一点已经排除了. 那么就直接进入到第二点方法内查看.

Message.recycleUnchecked()
为了更好的理解 我把 recycleUnchecked() obtain() 放在一起., 并省略一些不重要的代码

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

recycleUnchecked()
注意: 变量 nextsPool 都是 Message.

假设现在又调用了一遍 recycleUnchecked 这个方法, 之前第一遍调用 recycleUnchecked 中那个第一个可被复用的对象为 Message1, 依旧执行上面三步.

接着看 obtain(), 假设消息对象池中已经有两个对象了. 就是 Message1, Message2.

那么现在, m 就是单独的一个 Message 对象. sPool 消息对象池数量为 1, 对应的是 Message1, 并且 sPool.next = null , 因为从消息对象池中取出了 Message2 给了 m.
那么剩下的是什么操作呢, Message2 取出来了, 就要开始分发了, 还记得昨天分析的 Looper.loop() 方法吗, 分发完 Message2 后最后一步就是调用了 Message2.recycleUnchecked() 方法, 又将 Message2 放入了消息对象池了, 这样就完成了一次 Message 的复用.

(一开始消息对象池就是 null , 当我们第一次调用 Message.obtain() 的时候, 其实就是直接 new 了一个 Message 的. 然后在昨天学习的 Looper.loop() 方法的最后, 分发完消息后, 调用了 msg.recycleUnchecked() 将我们 newMessage 放入了消息对象池.)


 

1.2 public static Message obtain(Message orig)

public static Message obtain(Message orig) {
      Message m = obtain();
      m.what = orig.what;
      m.arg1 = orig.arg1;
      m.arg2 = orig.arg2;
      m.obj = orig.obj;
      m.replyTo = orig.replyTo;
      m.sendingUid = orig.sendingUid;
      if (orig.data != null) {
          m.data = new Bundle(orig.data);
      }
      m.target = orig.target;
      m.callback = orig.callback;
      return m;
  }
  • 解析

 

1.2 public static Message obtain(Handler h)

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;
    return m;
}
  • 解析

剩下几个 obtain 方法都基本类似, 都是先调用了obtain 无参函数, 在重置一些值. 这里就不再一一说明.

主要就是对无参 obtain 方法的理解, recycleUnchecked() 方法的理解以及消息对象池 sPool 的理解. 这三个方面, 还是要捋清楚的. 下一章一起学习分析 MessageQueue.

举报

相关推荐

0 条评论