Android-Handler源码解析-Message
源码版本:
- Handler:SDK-31
导航:
成员变量
// 标识Message
public int what;
// 存储简单数据,如果存储复杂的数据使用setData()方法。
public int arg1;
public int arg2;
// 发送给接收者的任意对象。
public Object obj;
// 回复给发送者,用于跨进程双向通信。发送message时给其replyTo赋值,接收到该message的进程,可以通过message.replyTo向发送方进程发送message,从而实现双向通信。
public Messenger replyTo;
public static final int UID_NONE = -1;
// 可选字段,表示发送消息的uid。这只对Messenger发布(跨进程发布)的消息有效;否则,它就是-1。
public int sendingUid = UID_NONE;
// 可选字段,指示导致该消息进入队列的uid。
public int workSourceUid = UID_NONE;
// 在用标记
/*package*/ static final int FLAG_IN_USE = 1 << 0;
// 异步标记
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
// copy的清空标记
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
// 标记位
@UnsupportedAppUsage
/*package*/ int flags;
// 执行时刻,时刻基于SystemClock.uptimeMillis。
@UnsupportedAppUsage
public long when;
// 存储复杂的数据
/*package*/ Bundle data;
// 消息的目标处理器Handler,即由此Handler处理此消息。
@UnsupportedAppUsage
/*package*/ Handler target;
// 表示要处理的任务Callback,由Runnable实现,通过handler.post Runnable的时候,Runnable会被存放在Message中,分发的时候再进行回调通知。
@UnsupportedAppUsage
/*package*/ Runnable callback;
// 下个消息,用于实现链表结构。
@UnsupportedAppUsage
/*package*/ Message next;
// 消息池的同步锁对象,用户消息池的消息获取(obtain()方法)和消息回收(recycleUnchecked()方法)。
/** @hide */
public static final Object sPoolSync = new Object();
// 消息池链表的头,用于操作(增删改查)链表。
private static Message sPool;
// 消息池中消息的数量
private static int sPoolSize = 0;
// 消息池中消息的最大数量,为50个。
private static final int MAX_POOL_SIZE = 50;
// 是否检查消息的回收(recycle()方法),默认true,如果检查并且有问题,则抛出异常。
private static boolean gCheckRecycle = true;
说明:
Message
为什么需要持有Handler
,因为Message
需要知道是哪个Handler
要处理它。Handler
相关介绍,请看Android-Handler源码解析-Handler。
创建Message
想要使用、发送Message
,首先要创建Message
,所以我们接下来看下它是如何被创建的。
new Message()
public Message() {
}
直接创建一个Message
对象,之后可以设置它的属性,未使用复用,不推荐使用。
Message.obtain()
Message.obtain()
public static Message obtain() {
// 使用同步保证线程安全,因为此方法可以在任意线程调用。
synchronized (sPoolSync) {
if (sPool != null) {
// 消息池头不为空,说明队列有内容,从中获取一条消息并返回。缓存的
Message m = sPool; // 消息池的Head元素
sPool = m.next; // 缓存池Head为下个元素(移除m)
m.next = null; // 断开链接
m.flags = 0; // 清除在用标记
sPoolSize--; // 池数量减1
return m; // 返回此消息
}
}
// 消息池头为空,说明队列没有内容,直接创建一条消息并返回。
return new Message();
}
Message.obtain()
为静态方法,内部用了全局消息池,使用了复用,推荐使用。
说明:
- 从消息池中获取消息,它会获取队列的第一条消息,并将此消息从此队列中移除。
Message.obtain(Message)
public static Message obtain(Message orig) {
// 获取消息
Message m = obtain();
// 将orig的值复制到新消息中
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
m.workSourceUid = orig.workSourceUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
与Message.obtain()
相同,但将现有Message
(包括其target
)的值复制到新的消息中。
Message.obtain(Handler)
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
与Message.obtain()
相同,但设置了target
成员的值。
Message.obtain(Handler, Runnable)
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
与Message.obtain()
相同,但设置了target
和callback
成员的值。
Message.obtain(Handler, int)
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}Message.
与Message.obtain()
相同,但设置了target
和what
成员的值。
Message.obtain(Handler, int, Object)
public static Message obtain(Handler h, int what, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.obj = obj;
return m;
}
与Message.obtain()
相同,但设置了target
、what
和obj
成员的值。
Message.obtain(Handler, int, int, int)
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;
}
与Message.obtain()
相同,但设置了target
、what
、arg1
和arg2
成员的值。
Message.obtain(Handler, int, int, int, Object)
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;
}
与Message.obtain()
相同,但设置了target
、what
、arg1
、arg2
和obj
成员的值。
小结
Message
的创建,有两种方式:new Message
、Message.obtain()
,Message.obtain()
内部使用了复用,推荐使用。
回收Message
由于Message.obtain()
方式创建内部使用了复用,所以我们接下来看下它是如何被回收的。
recycle()
public void recycle() {
// 判断是否在使用中
if (isInUse()) {
// 在使用中,直接返回,不进行回收。
if (gCheckRecycle) {
// gCheckRecycle为true,为检查消息的回收,并且回收时正在使用(有问题),则抛出异常。
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
// 不在使用中,进行回收。
recycleUnchecked();
}
recycle()
方法,为检查回收,如果此消息处于使用状态,则不进行回收,否则调用recycleUnchecked()
直接进行回收。
isInUse()
方法的使用看后面-其它-InUse,我们接下来看下gCheckRecycle
属性,它在updateCheckRecycle()
方法内进行更改。
public static void updateCheckRecycle(int targetSdkVersion) {
if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
// 小于SDK 21,为false。
gCheckRecycle = false;
}
}
updateCheckRecycle()
方法,为更新gCheckRecycle
的值。gCheckRecycle
默认为true
,updateCheckRecycle()
方法在ActivityThread
的handleBindApplication()
方法(绑定App)内调用,即gCheckRecycle
在targetSdkVersion
小于21
为false
(不检查),大于、等于21
为ture
(检查)。
recycleUnchecked()
void recycleUnchecked() {
// 将此消息标记为在用,并判断是否添加到消息池中。
// 清除所有属性
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) {
// 小于最大值,则进行添加,把此Message添加到链表的头部。
next = sPool; // 此Message的next指向链表的Head
sPool = this; // 链表的Head为此Message
sPoolSize++; // 消息池数量加1
}
}
}
recycleUnchecked()
方法,为不检查回收,会清除消息的所有属性,以完成释放,并判断如果消息池的消息数量没有到达最大数量,则进行添加,以完成回收。
说明:
- 从消息池中添加消息,它会将此消息添加到队列的头部,并将此消息作为新的头。
小结
Message
的回收,有两种方式:recycle()
检查回收、recycleUnchecked()
不检查回收。recycle()
,判断了消息如果处于使用状态,则不进行回收,否则调用recycleUnchecked()
进行回收。recycleUnchecked()
,会清除消息的所有属性,并判断如果消息池数量没到消息池最大数量,则进行添加(添加到消息池的链表的头部)。
跨进程通讯
由于Message
实现了Parcelable
接口,所以Message
可以跨进程传输,所以我们接下来看下它的具体实现。
writeToParcel()
public void writeToParcel(Parcel dest, int flags) {
if (callback != null) {
// callback不能跨进程传输,抛出异常。
throw new RuntimeException(
"Can't marshal callbacks across processes.");
}
dest.writeInt(what);
dest.writeInt(arg1);
dest.writeInt(arg2);
if (obj != null) {
// obj不为空,跨进程传输任意对象,必须实现Parcelable接口,否则抛出异常。
try {
Parcelable p = (Parcelable)obj;
dest.writeInt(1); // 传入1,标记有存入obj对象。
dest.writeParcelable(p, flags); // 写入obj对象
} catch (ClassCastException e) {
throw new RuntimeException(
"Can't marshal non-Parcelable objects across processes.");
}
} else {
dest.writeInt(0); // 传入0,标记没有存入obj对象。
}
dest.writeLong(when);
dest.writeBundle(data);
Messenger.writeMessengerOrNullToParcel(replyTo, dest); // 使用Messenger进行写入
dest.writeInt(sendingUid);
dest.writeInt(workSourceUid);
}
readFromParcel()
private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
if (source.readInt() != 0) {
// 有存入obj对象,进行读取。
obj = source.readParcelable(getClass().getClassLoader());
}
when = source.readLong();
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source); // 使用Messenger进行读取
sendingUid = source.readInt();
workSourceUid = source.readInt();
}
小结
Message
的跨进程通讯,callback
不能有值,obj
如有值则必须实现Parcelable
接口。
属性set、get方法
Message
的when
、target
、callback
、data
属性对外提供了set
、get
方法,我们接下来看下它们的实现。
when
public long getWhen() {
return when;
}
target
public void setTarget(Handler target) {
this.target = target;
}
public Handler getTarget() {
return target;
}
callback
public Runnable getCallback() {
return callback;
}
data
public Bundle getData() {
if (data == null) {
data = new Bundle();
}
return data;
}
public Bundle peekData() {
return data;
}
public void setData(Bundle data) {
this.data = data;
}
其它
copyFrom()
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;
this.workSourceUid = o.workSourceUid;
if (o.data != null) {
this.data = (Bundle) o.data.clone();
} else {
this.data = null;
}
}
将指定Message
的属性,复制到当前Message
中。
sendToTarget()
public void sendToTarget() {
target.sendMessage(this);
}
将此消息发送给自己的目标Handler
,如果未设置此字段,则抛出空指针异常。
asynchronous(异步)
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
isAsynchronous()
方法,判断消息是否是异步的,如果是,则返回true
。
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
setAsynchronous()
方法,设置消息是否是异步的,这意味着它不受Looper
同步屏障的影响。
说明:
- 消息,分为同步消息和异步消息,默认为同步消息。
- 同步屏障,会屏障同步消息,确保只有异步消息执行,详细请看Android-Handler源码解析-MessageQueue-同步屏障。
- 某些操作(比如视图失效)可能会在
Looper
的消息队列中引入同步屏障,以阻止后续消息在满足某些条件之前被传递。在view invalidation
的情况下,调用android.view.View.invalidate
后发布的消息会被一个同步屏障挂起,直到下一帧准备被绘制。同步屏障确保在恢复之前完全处理了无效请求。- 异步消息免于同步屏障。它们通常表示中断、输入事件和其它必须独立处理的信号,即使其它工作已经暂停。
- 请注意,异步消息可能是按同步消息的顺序交付的,尽管它们之间总是按顺序交付的。如果这些消息的相对顺序很重要,那么它们可能一开始就不应该是异步的,谨慎使用。
InUse
/*package*/ boolean isInUse() {
return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
isInUse()
方法,判断是否处于使用状态。
/*package*/ void markInUse() {
flags |= FLAG_IN_USE;
}
markInUse()
方法,标记处于使用状态。
总结
以上就是Handler
源码的Message
源码部分,Handler
其它源码部分看下面导航。之后会出其它Android
源码系列,请及时关注。如果你有什么问题,大家评论区见!
导航:
最后推荐一下我的网站,开发者的技术博客: devbolg.cn ,目前包含android相关的技术,之后会面向全部开发者,欢迎大家来体验!
网友评论