其实是想跟着书看的,自己先大概浏览了一下Handler的源码,关于Message、MessageQueue、Looper。然后又写了一下Handler在两个子线程之间传递Message的例子,发现服务端在接收消息的时候它所持有的Handler必须是static类型的才能在客户端获取到。这又让我想到才看的java虚拟机内存运行时的四大部分,Handler到底是在java堆中还是在java虚拟机栈中,好懵,总感觉自己看的东西不能和实际结合,问题出来总是不敢确定一个准确的答案。在写代码的过程中对sendToTarget和sendMessage两个方法又有点迷,到底是咋一回事,所以先把Message看看,看看这个类的一些重要的方法。
Message
Message的成员变量
1. 用于传递数据的变量
- public int what
what很常用,一般在handleMessage中通过调用msg.what来判断接收的消息是什么,也就是判断是哪个线程发过来的哪个类型的消息
- public int arg1
- public int arg2
arg1和arg2都是可以作为整数类型传递的。比方说msg.arg1=1;接收方int i = msg.arg1获得就可以。这两个我几乎没用过,因为有时候传的数据比较多,就放到Bundle里了,有这两个的原因就是一种低成本的方案,减少了使用创建Bundle对象,再通过setData方法去传递几个整数数据。
- public Object obj
Object可以传递任意类型的对象,我通常都用它传递了一个简单的字符串去更新ui线程的数据。
- public Messenger replyTo
这个通常在进程间通信的时候使用。Messenger作为信使传递的数据都是轻量级的。使用replyTo的时候一般是在服务器给客户端回信的时候需要获得客户端的Messenger,客户端在给服务器传递消息的时候可以通过msg.replyTo = mClientMessenger的方式传递给服务端,服务端拿到后就可以把消息传递给客户端了。
- Bundle data
Bundle传递数据用的比较多,这里就简单展示下。
//线程1
Bundle bundle = new Bundle();
bundle.putString("key","hello");
msg.setData(bundle);
//线程2
Bundle bundle = msg.getData();
String str = bundle.getString("key");
2. 其他变量
- static final int FLAG_IN_USE = 1 << 0:标记消息被使用
- static final int FLAG_ASYNCHRONOUS = 1 << 1:标记消息是异步的
- int flags:用来标记当前Message是否可用
- long when:发送之后,何时能被处理
- Handler target:表示消息被哪个handler处理
- Runnable callback:用handler.post一个runnable,这个runnable是被包装成Message对象的
- Message next:下一个可用的消息
- public static final Object sPoolSync = new Object():sPoolSync充当锁的作用,避免多线程争抢资源
- private static Message sPool:代表消息池的头部
- private static int sPoolSize = 0:当前可用消息池个数
- private static final int MAX_POOL_SIZE = 50:最大消息池个数
Message的几个方法
Message#obtain()
Message msg = Message.obtain();这个方法是我们常用的看下它的源码
public static Message obtain() {
synchronized (sPoolSync) {
//Message中没有说明这个sPool是什么时候初始化的
if (sPool != null) {
Message m = sPool;//m指向消息头 可以把它当作一个栈的结构
sPool = m.next;//sPool从栈顶向下移动一个单位
m.next = null;//栈顶的元素出栈
m.flags = 0; // 设置m此时不可用
sPoolSize--;//消息池中可用的Message个数减一
return m;//将栈顶的Message返回
}
}
return new Message();
}
Message可以通过new Message()来获取一个Message对象,但是最好的方式是调用Message.obtain()来获取。因为当它被回收的时会被放入一个对象池中。所以obtain方法中当消息池中没有Message的时候会new Message(),否则就会从消息池中取出一个可用的Message对象。具体的流程在注释中标出,Message消息池其实是一个链表结构,但是实现的过程可以认为是一个栈,取出一个Message其实就是一个弹栈的过程。
Message#recycleUnchecked
既然有从消息池取出Message的过程,那么就有Message被回收的过程,这个过程就在recycleUnchecked方法中
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
recycleUnchecked方法是在recycle方法中被调用的。
recycle方法通过isInUse()方法判断flag是否可用来执行回收方法。
void recycleUnchecked() {
//将flag标志位设置为可用状态
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) {
//判断当前的sPoolSize可复用的Message个数是否小于最大Message个数(50个)
if (sPoolSize < MAX_POOL_SIZE) {
//执行入栈操作 复用池Message个数加一
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
回收可能正在使用的消息。在处理排队的消息时由MessageQueue和Looper内部使用。
Message#sendToTarget
public void sendToTarget() {
target.sendMessage(this);
}
在发送消息的时候我们也可以使用sendToTarget方法,只不过这个方法是Message对象调用的。它的内部实现了handler.sendMessage(msg)方法。
public void setTarget(Handler target) {
this.target = target;
}
target就是handler
Message存储数据的方式
public final class Message implements Parcelable
Message对象实现了Parcelable接口,说明Message内部运用了序列化的方法。
public static final Parcelable.Creator<Message> CREATOR
= new Parcelable.Creator<Message>() {
public Message createFromParcel(Parcel source) {
Message msg = Message.obtain();
//读取Parcel中的数据
msg.readFromParcel(source);
return msg;
}
public Message[] newArray(int size) {
return new Message[size];
}
};
public int describeContents() {
return 0;
}
//将数据写入Parcel
public void writeToParcel(Parcel dest, int flags) {
if (callback != null) {
throw new RuntimeException(
"Can't marshal callbacks across processes.");
}
dest.writeInt(what);
dest.writeInt(arg1);
dest.writeInt(arg2);
if (obj != null) {
try {
Parcelable p = (Parcelable)obj;
dest.writeInt(1);
dest.writeParcelable(p, flags);
} catch (ClassCastException e) {
throw new RuntimeException(
"Can't marshal non-Parcelable objects across processes.");
}
} else {
dest.writeInt(0);
}
dest.writeLong(when);
dest.writeBundle(data);
Messenger.writeMessengerOrNullToParcel(replyTo, dest);
dest.writeInt(sendingUid);
}
private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
if (source.readInt() != 0) {
obj = source.readParcelable(getClass().getClassLoader());
}
when = source.readLong();
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
}
总结
- Message作为消息实体可以通过what、arg、obj、Bundle等携带想要发送的数据
- Message内部维护一个消息池,这个消息池的最大容量MAX_POOL_SIZE是50。存储Message的结构是一个链表,obtain方法获得的Message对象就是当前链表的第一个Message。可以把这个链表操作的方法想成一个栈的结构。
- Message.obtain方法和Handler.obtainMessage方法等价。
这是我第一次自己完全分析的一篇源码,之前分析源码或多或少都有参考。有什么不足的地方还请大家指正。加油!!!
网友评论