Handler消息机制(一)Message复用原理
Handler消息机制(一)Message复用原理
[图片上传失败...(image-324c66-1686817542469)]
jaymzyang[图片上传失败...(image-e2e707-1686817542466)]
<time datetime="2021-03-04T01:49:16.000Z" title="Thu Mar 04 2021 09:49:16 GMT+0800 (中国标准时间)" class="time" data-v-7383b867="" style="letter-spacing: 1px;">2021年03月04日 09:49 </time>· 阅读 1772
本篇文章分享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 消息对象
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
Message msg = new Message(); // new新的对象 msg.what = 100; msg.obj = obj; mHandler.sendMessage(msg);
</pre>
方式二:复用池中的消息对象
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
Message msg = mHandler.obtainMessage(); msg.what = 100; msg.obj = obj; mHandler.sendMessage(msg);
</pre>
or
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
Message msg = Message.obtain(mHandler); msg.what = 100; msg.obj = obj; mHandler.sendMessage(msg);
</pre>
2. Message的复用原理
首先根据Message数据结构的定义,可以看出Message采用单向链表结构实现Message对象的管理,通过sPool记录表头数据。 为了理解复用原理,摘取Message如下关键参数,Message伪代码结构:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
arduino
复制代码
int sPoolSize, // 池中Message数量 Message sPool, // 当前Message,即表头 Message next, // 当前Message的下一个节点 int what... // 其他参数
</pre>
2.1 回收message对象
最新回收的message放入表头,通过sPool来记录当前表头的message,sPool的next节点是回收前的sPool记录的message
源码Message中回收消息 recycleUnchecked:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
`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++;
}
}
}` </pre>
抽象伪代码:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
next= sPool; // next有可能为null sPool = this; sPoolSize++;
</pre>
假设Message消息回收的顺序:msg1 -> msg2 -> msg3
第一步,消息msg1回收时:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
msg1.next = sPool; // sPool 是null sPool = msg1;
</pre>
第二步,消息msg2回收时:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
msg2.next = sPool; // sPool 是msg1 sPool = msg2;
</pre>
第三步,消息msg3回收时:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
msg3.next = sPool; // sPool 是msg2 sPool = msg3;
</pre>
回收后的链表结构是:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
vbscript
复制代码
sPool = msg3,msg3.next -> msg2,msg2.next -> msg1,msg1.next -> null
</pre>
2.2 获取对象池消息
- 如果sPool为null时,新建一个Message
- 如果sPool不为null时,从对象池中获取Message对象,每次均从表头查询,即sPool记录的表头消息。
源码Handler中获取message:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
kotlin
复制代码
public final Message obtainMessage() { return Message.obtain(this); }
</pre>
源码Message中获取message:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
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(); }
</pre>
抽象伪代码:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
if(null != sPool) { Message m = sPool; sPool = m.next sPoolSzie--; return m; } return new Message();
</pre>
假设消息链表结构是(表头是msg3):
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
vbscript
复制代码
sPool = msg3,msg3.next -> msg2,msg2.next -> msg1,msg1.next -> null
</pre>
第一步,获取消息msg3:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
Message m = sPool; // sPool 是msg3 sPool = m.next; // m.next即msg3.next 是msg2,sPool当前指向msg2 return m; // m即表头msg1
</pre>
第二步,获取消息msg2:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
Message m = sPool; // sPool 是msg2 sPool = m.next; // m.next即msg2.next 是msg1 return m; // m即表头msg2
</pre>
第三步,获取消息msg1:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
ini
复制代码
Message m = sPool; // sPool 是msg1 sPool = m.next; // m.next即msg1.next 是null return m; // m即表头msg1
</pre>
第四步,对象池中无消息时:
<pre style="font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 1em; position: relative; overflow: auto; line-height: 1.75; color: rgb(51, 51, 51); background: rgb(248, 248, 248);">
arduino
复制代码
return new Message();
</pre>
通过对Message对象复用的原理进行分析,其实在复用对象时进行的消息回收和获取,采用了栈的思想来实现,即先入后出(FILO)的原则,即最新回收的对象会被优先再次使用。相信看到这,大家就会明白Message消息的复用机制了,希望该文章可以帮助小伙伴们在开发过程中,合理的使用Message消息啦!
结语:在开发中不会频繁的使用消息对象时,直接new即可;如果需要频繁的使用消息对象,可以采用复用的方式,减少new对象的消耗,更高效的处理消息事件。