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
进行赋值.
- 上面的无参
obtain
方法中. -
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()
注意: 变量 next
与 sPool
都是 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()
将我们 new
的 Message
放入了消息对象池.)
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
.