- 定义一个可以发送给Handler的描述和任意数据对象的消息。
- 此对象包含两个额外的int字段和一个额外的对象字段,这样就可以使用在很多情况下不用做分配工作。
- 尽管Message的构造器是公开的,但是获取Message对象的最好方法是调用Message.obtain()或者Handler.obtainMessage(),这样是从一个可回收的对象池中获取Message对象。
1 Message成员变量解析
1.1 成员变量 what
- Message用一个标志来区分不同消息的身份,不同的Handler使用相同的消息不会弄混,一般使用16进制形式来表示
代码在Message.java 39行
用户定义的Message的标识符用以分辨消息的内容。Hander拥有自己的消息代码的命名空间,因此你不用担心与其他的Handler冲突。
1.2 成员变量 arg1和arg2
- arg1和arg2都是Message类的可选变量,可以用来存放两个整数值,不用访问obj对象就能读取的变量。
代码在Message.java 46行
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg2;
如果你仅仅是保存几个整形的数值,相对于使用setData()方法,使用arg1和arg2是较低成本的替代方案。
1.3 成员变量 obj
- obj 用来保存对象
代码在Message.java 65行
/**
* An arbitrary object to send to the recipient. When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application). For other data transfer use
* {@link #setData}.
*
* <p>Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
public Object obj;
- 将一个独立的对象发送给接收者。当使用Messenger去送法消息,并且这个对象包含Parcelable类的时候,它必须是非空的。对于其他数据的传输,建议使用setData()方法
- 请注意,在Android系统版本FROYO(2.2)之前不支持Parcelable对象。
1.4 其他成员变量
// 回复跨进程的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;
2 获取Message对象
2.1 Message构造函数
代码在Message.java 475行
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
*/
public Message() {
}
2.2 Message.obtain()方法
我们来看下Message.java 类的结构图
Message.居然有8个obtain函数
分为两大类
- 无参的obtain()方法
- 有参的obtain()方法
3 Message的消息对象池和无参的obtain()方法
3.1 无参的obtain()
代码在Message.java 122行
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
// 保证线程安全
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
// flags为移除使用标志
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
- 从全局的pool返回一个实例化的Message对象。这样可以避免我们重新创建冗余的对象。
3.2 sPool
代码在Message.java 111行
private static Message sPool;
- 好像也没什么嘛?就是一个Message对象而已,所以sPool默认是null。
- 除了public static Message obtain()里面的if (sPool != null) {}里面外,还有recycleUnchecked给sPool赋值了
3.3 recycleUnchecked()
代码在Message.java 291行
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
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++;
}
}
}
3.4 消息对象池的理解
把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;
}
}
}
3.4.1 recycleUnchecked()的理解
假设消息对象池为空,从new message开始,到这个message被取出使用后,准备回收 先来看recycleUnchecked()方法
- 第一步,next=sPool,因为消息对象池为空,所以此时sPool为null,同时next也为null。
- 第二步,spool = this,将当前这个message作为消息对象池中下一个被复用的对象。
- 第三步,sPoolSize++,默认为0,此时为1,将消息对象池的数量+1,这个数量依然是全系统共共享的。
这时候假设又调用了,这个方法,之前的原来的第一个Message对象我假设定位以为message1,依旧走到上面的循环。
- 第一步,next=sPool,因为消息对象池为message1,所以此时sPool为message1,同时next也为message1。
- 第二步,sPool = this,将当前这个message作为消息对象池中下一个被复用的对象。
- 第三步,sPoolSize++,此时为1,将消息对象池的数量+1,sPoolSize为2,这个数量依然是全系统共共享的。
以此类推,直到sPoolSize=50(MAX_POOL_SIZE = 50)
3.4.2 obtain()的理解
假设上面已经回收了一个Message对象,又从这里获取一个message,看看会发生什么?
- 第一步,判断sPool是否为空,如果消息对象池为空,则直接new Message并返回
- 第二步,Message m = sPool,将消息对象池中的对象取出来,为m。
- 第三步,sPool = m.next,将消息对象池中的下一个可以复用的Message对象(m.next)赋值为消息对象池中的当前对象。(如果消息对象池就之前就一个,则此时sPool=null)
- 第四步,将m.next置为null,因为之前已经把这个对象取出来了,所以无所谓了。
- 第五步,m.flags = 0,设置m的标记位,标记位正在被使用
- 第六步,sPoolSize--,因为已经把m取出了,这时候要把消息对象池的容量减一。
将sPool看成一个指针,通过next来将对象组成一个链表,因为每次只需要从池子里拿出一个对象,所以不需要关心池子里具体有多少个对象,而是拿出当前这个sPool所指向的这个对象就可以了,sPool从思路上理解就是通过左右移动来完成复用和回收
4 obtain()有参函数解析
4.1 ② public static Message obtain(Message orig)
代码在Message.java 142行
/**
* Same as {@link #obtain()}, but copies the values of an existing
* message (including its target) into the new one.
* @param orig Original message to copy.
* @return A Message object from the global pool.
*/
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;
}
4.2 public static Message obtain(Handler h)
代码在Message.java 164行
/**
* Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.
* @param h Handler to assign to the returned Message object's <em>target</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
4.3 public static Message obtain(Handler h, Runnable callback)
代码在Message.java 178行
/**
* Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
* the Message that is returned.
* @param h Handler to assign to the returned Message object's <em>target</em> member.
* @param callback Runnable that will execute when the message is handled.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
4.4 ⑤ public static Message obtain(Handler h, int what)
代码在Message.java 193行
/**
* Same as {@link #obtain()}, but sets the values for both <em>target</em> and
* <em>what</em> members on the Message.
* @param h Value to assign to the <em>target</em> member.
* @param what Value to assign to the <em>what</em> member.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}
4.5 #### public static Message obtain(Handler h, int what, Object obj)
代码在Message.java 209行
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
* members.
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param obj The <em>object</em> method to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.obj = obj;
return m;
}
4.6 public static Message obtain(Handler h, int what, int arg1, int arg2)
代码在Message.java 228行
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
* <em>arg1</em>, and <em>arg2</em> members.
*
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param arg1 The <em>arg1</em> value to set.
* @param arg2 The <em>arg2</em> value to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what, int arg1, int arg2) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
return m;
}
4.7 ⑧ public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)
代码在Message.java 249行
/**
* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
* <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
*
* @param h The <em>target</em> value to set.
* @param what The <em>what</em> value to set.
* @param arg1 The <em>arg1</em> value to set.
* @param arg2 The <em>arg2</em> value to set.
* @param obj The <em>obj</em> value to set.
* @return A Message object from the global pool.
*/
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
5 Message的 浅拷贝
Message的浅拷贝 就是copyFrom(Message o)函数
代码在Message.java 320行
/**
* Make this message like o. Performs a shallow copy of the data field.
* Does not copy the linked list fields, nor the timestamp or
* target/callback of the original message.
*/
public void copyFrom(Message o) {
this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
this.what = o.what;
this.arg1 = o.arg1;
this.arg2 = o.arg2;
this.obj = o.obj;
this.replyTo = o.replyTo;
this.sendingUid = o.sendingUid;
if (o.data != null) {
this.data = (Bundle) o.data.clone();
} else {
this.data = null;
}
}
- 其实从本质上看就是从一个消息体复制到另一个消息体。
- 从代码中可以看出大部分数据确实都是浅拷贝,但是对于data这个Bundle类型的成员变量却进行了深拷贝,所以说该方法是一个浅拷贝方法感觉也不是很贴切。