Message中可以携带的信息
Message中可以携带的数据比较丰富,下面对一些常用的数据进行了分析。
/**
* 用户定义的消息代码,以便当接受到消息是关于什么的。其中每个Hanler都有自己的命名控件,不用担心会冲突
*/
public int what;
/**
* 如果你只想存很少的整形数据,那么可以考虑使用arg1与arg2,
* 如果需要传输很多数据可以使用Message中的setData(Bundle bundle)
*/
public int arg1;
/**
* 如果你只想存很少的整形数据,那么可以考虑使用arg1与arg2,
* 如果需要传输很多数据可以使用Message中的setData(Bundle bundle)
*/
public int arg2;
/**
* 发送给接受方的任意对象,在使用跨进程的时候要注意obj不能为null
*/
public Object obj;
/**
* 在使用跨进程通信Messenger时,可以确定需要谁来接收
*/
public Messenger replyTo;
/**
* 在使用跨进程通信Messenger时,可以确定需要发消息的uid
*/
public int sendingUid = -1;
/**
* 如果数据比较多,可以直接使用Bundle进行数据的传递
*/
Bundle data;
创建消息的方式
官方建议使用Message.obtain()系列方法来获取Message实例,因为其Message实例是直接从Handler的消息池中获取的,可以循环利用,不必另外开辟内存空间,效率比直接使用new Message()创建实例要高。其中具体创建消息的方式,我已经为大家分好类了。具体分类如下:
//无参数
public static Message obtain() {...}
//带Messag参数
public static Message obtain(Message orig) {}
//带Handler参数
public static Message obtain(Handler h) {}
public static Message obtain(Handler h, Runnable callback){}
public static Message obtain(Handler h, int what){}
public static Message obtain(Handler h, int what, Object obj){}
public static Message obtain(Handler h, int what, int arg1, int arg2){}
public static Message obtain(Handler h, int what,int arg1, int arg2, Object obj) {}
其中在Message的obtain带参数的方法中,内部都会调用无参的obtain()方法来获取消息后。然后并根据其传入的参数,对Message进行赋值。(关于具体的obtain方法会在下方消息池实现原理中具体描述)
消息池实现原理
既然官方建议使用消息池来获取消息,那么在了解其内部机制之前,我们来看看Message中的消息池的设计。具体代码如下:
private static final Object sPoolSync = new Object();//控制获取从消息池中获取消息。保证线程安全
private static Message sPool;//消息池
private static int sPoolSize = 0;//消息池中回收的消息数量
private static final int MAX_POOL_SIZE = 50;//消息池最大容量
// sometimes we store linked lists of these things
@UnsupportedAppUsage
/*package*/ Message next;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; //重新标识当前Message没有使用过
sPoolSize--;
return m;
}
}
return new Message();//如果为空直接返回
}
从中我们发现如果sPool如果不为null,则返回直接new一个Message返回,整个方法结束,那么sPool是什么,sPool是一个message,从源码中我们可以发现sPool其实就相当于一个头指针,指向缓存池中第一个缓存的Message,如果sPool不为null则说明缓存池中存在空闲的Message,返回缓存池中空闲的message,然后sPool执行下一个缓存message对象,然后将msg.next重置为0,整体代码过下来,我们发现Message的缓存池其实就是用了一个数据结构-单向链表。具体流程如图:
这块就很明显是消息池的取出了,那么它的存是在哪里呢,全局搜sPool,我们发现在recycleUncheck中有实现
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
@UnsupportedAppUsage
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 = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
然后这块代码又是在recycle中调用的
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
这块首先判断消息是否在使用之中,如果在使用之中,继续判断gCheckRecycle,gCheckRecycle的默认值是true,如果不在使用之中,最后会走进recycleUnchecked。
然后来分析recycleUnChecked
一开始,把这个消息所有成员赋值成最初的状态,FLAG_IN_USE的值是1一开始说了Message的flags表示这个Message有没有在使用,1表示在池中,等待复用,0表示正在被使用。重点看同步锁中的代码。
假设全局池没有元素时,我们将第一个消息放到池中,sPool一开始是NULL,next指向了sPool,所以此时的消息的sPool和next都是NULL,然后sPool指向当前的Message对象,最后池的数量加1。大致如下图。
假设有来个消息m2,在走一遍同步锁中的代码,此时全局池的状态如下图所示。
其他几个类似