本篇文章分享Handler消息机制中的Message的使用和Message的复用原理。
该系列的其他文章
- Handler消息机制(一)Message复用原理
- Handler消息机制(二)ThreadLocal原理
- Handler消息机制(三)MessageQueue原理
- Handler消息机制(四)Looper原理
- Handler消息机制(五)Handler原理
包含以下两个部分:
- Message在Handler的使用方式
- Message的复用原理
在Android开发中,使用Handler实现线程间通信,非常方便,当频繁的进行消息通信时,每次都去new消息对象,在创建对象时对系统资源的占用,同时GC频繁的回收对象等,对内存和系统性能还是会有一定影响的,当频繁的通过Handler处理消息事件时,推荐obtainMessage()的方式获取message实例,该方式在对象池中获取message,运用了享元模式的设计思想。
仅了解Message的使用方式之后,更应该知其然知其所依然,关于如何实现Message的复用,后面我会从源码层面分析复用的实现思想。
1. Message在Handler中的使用方式
Message在Handler中的使用非常简单,参考下面两种使用方式: 方式一:new 消息对象
Message msg = new Message(); // new新的对象
msg.what = 100;
msg.obj = obj;
mHandler.sendMessage(msg);
方式二:复用池中的消息对象
Message msg = mHandler.obtainMessage();
msg.what = 100;
msg.obj = obj;
mHandler.sendMessage(msg);
or
Message msg = Message.obtain(mHandler);
msg.what = 100;
msg.obj = obj;
mHandler.sendMessage(msg);
2. Message的复用原理
首先根据Message数据结构的定义,可以看出Message采用单向链表结构实现Message对象的管理,通过sPool记录表头数据。 为了理解复用原理,摘取Message如下关键参数,Message伪代码结构:
int sPoolSize, // 池中Message数量
Message sPool, // 当前Message,即表头
Message next, // 当前Message的下一个节点
int what... // 其他参数
2.1 回收message对象
最新回收的message放入表头,通过sPool来记录当前表头的message,sPool的next节点是回收前的sPool记录的message
源码Message中回收消息 recycleUnchecked:
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 = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
抽象伪代码:
next= sPool; // next有可能为null
sPool = this;
sPoolSize++;
假设Message消息回收的顺序:msg1 -> msg2 -> msg3
第一步,消息msg1回收时:
msg1.next = sPool; // sPool 是null
sPool = msg1;
第二步,消息msg2回收时:
msg2.next = sPool; // sPool 是msg1
sPool = msg2;
第三步,消息msg3回收时:
msg3.next = sPool; // sPool 是msg2
sPool = msg3;
回收后的链表结构是:
sPool = msg3,msg3.next -> msg2,msg2.next -> msg1,msg1.next -> null
2.2 获取对象池消息
- 如果sPool为null时,新建一个Message
- 如果sPool不为null时,从对象池中获取Message对象,每次均从表头查询,即sPool记录的表头消息。
源码Handler中获取message:
public final Message obtainMessage() {
return Message.obtain(this);
}
源码Message中获取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();
}
抽象伪代码:
if(null != sPool) {
Message m = sPool;
sPool = m.next
sPoolSzie--;
return m;
}
return new Message();
假设消息链表结构是(表头是msg3):
sPool = msg3,msg3.next -> msg2,msg2.next -> msg1,msg1.next -> null
第一步,获取消息msg3:
Message m = sPool; // sPool 是msg3
sPool = m.next; // m.next即msg3.next 是msg2,sPool当前指向msg2
return m; // m即表头msg1
第二步,获取消息msg2:
Message m = sPool; // sPool 是msg2
sPool = m.next; // m.next即msg2.next 是msg1
return m; // m即表头msg2
第三步,获取消息msg1:
Message m = sPool; // sPool 是msg1
sPool = m.next; // m.next即msg1.next 是null
return m; // m即表头msg1
第四步,对象池中无消息时:
return new Message();
通过对Message对象复用的原理进行分析,其实在复用对象时进行的消息回收和获取,采用了栈的思想来实现,即先入后出(FILO)的原则,即最新回收的对象会被优先再次使用。相信看到这,大家就会明白Message消息的复用机制了,希望该文章可以帮助小伙伴们在开发过程中,合理的使用Message消息啦!
结语:在开发中不会频繁的使用消息对象时,直接new即可;如果需要频繁的使用消息对象,可以采用复用的方式,减少new对象的消耗,更高效的处理消息事件。