

详细讲解Android中的Message的源码
source link: http://www.androidchina.net/1500.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

相信大家对于Android中的Handler是在为熟悉不过了,但是要知道,Handler就其本身而言只是一个壳子,真正在内部起到作用的是Message这个类,对于Message这个类,相信大家也不会陌生,正如大家经常用到的Message.obtain()的方法一样。但是大家又是否知道obtain()方法里面为我们做了哪些操作了,下面我就带领大家进行Message的王国,去一探究竟吧。
首先映入眼帘的是这样的一行代码:
public
final
class
Message
implements
Parcelable
不用多说,Message实现了Parcelable的接口,也就是说经过Message封装的数据,可以通过Intent与IPC进行传输。既然实现了Parcelable接口,那么在Message方法中必不可少这三个方法:1)writeToParcel 2)describeContents 3)createFromParcel。
下面我们需要关注的四个成员变量分别是:
1)public int what
2)public int arg1
3)public int arg2
4)public Object obj
我们经常是用到这样的几个参数,但是其真实的含义是否真正的理解呢?只要google的内部的注释才是真正的可靠的。
1)用户定义消息的识别码,以便于系统识别出当前的消息是关于什么的。由于每一个Handle对于自己的消息的识别码都有自己的命名空间。所以我们也就不用担心各个不同的Handler之间会存在冲突的情况。
2)其中第二个参数与第三个参数的意义是一样的,注释上是这样说明的,这两个参数,如果用户只是需要传输简单的int类型的数据,相比较于setData(Bundle bundle),代价更低。
3)第四个参数,按照注释上的说明,是这样理解的:这是一个发送给接受者的一个随意的数据,如果使用Messager来进行发送数据进行跨进程的通信,那么当前的obj如果实现了Parcelable就一定不能够为空指针,对于其他的数据的传输,我们一般使用setData方法就可以了。但是需要注意的是,对于高于android.os.Build.VERSION_CODES#FROYO的版本,这里的Parcelable对象是不支持的。
上面的变量讲完了以后,接下来,我们还需要讲解另外的一组常量的定义:
private
static
final
Object sPoolSync =
new
Object();
private
static
Message sPool;
private
static
int
sPoolSize =
0
;
//池塘里最大的尺寸
private
static
final
int
MAX_POOL_SIZE =
50
;
乍一看,大家可能不理解上面四个变量的定义,如果我提醒一下大家,Android中的Message是可以重用的,那么相信大家就能够大致猜测到这四个变量的含义了。
1、第一个变量其实就是充当锁的作用,避免多线程争抢资源,导致脏数据
2、sPool这个变量可以理解为消息队列的头部的指针
3、sPoolSize是当前的消息队列的长度
4、定义消息队列缓存消息的最大的长度。
Ok,到这里,Message的成员变量已经讲解完毕,接下来主要是讲解其中的Api方法。
第一个方法就是大家经常用到的obtain方法,而且不带任何的参数:
public
static
Message obtain() {
synchronized
(sPoolSync) {
if
(sPool !=
null
) {
Message m = sPool;
sPool = m.next;
m.next =
null
;
sPoolSize--;
return
m;
}
}
return
new
CustomMessage();
}
首先为了避免多线程进行争抢资源,给sPoolSync进行加锁。首先判断当前的队列的指针是否为空,如果当前的指针已经不为空,当前的队列的头部的消息就是可以重用并且被取出,那么当前的队列的头指针指向当前的消息的下一个消息,也就是m.next,同时把取出的消息的尾部指针置为null,队列的长度减1.
第二个方法:
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;
if
(orig.data !=
null
) {
m.data =
new
Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return
m;
}
我们可以看到,这个方法相对于上面的方法多了一个orig的参数,从上面的代码我们可以看到,首先从队列中取出Message的对象,然后对其中的参数的对象的各个数据进行逐一的拷贝,并最终返回对象。
第三个方法如下:
public
static
Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return
m;
}
不用多说,这个函数中为当前创建的消息指定了一个Handler对象,因为我们知道Handler是Message的最终的目的地。
public
static
Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return
m;
}
相对于上面的方法,这里面多了一行m.callback = callback,按照注释上面的说明,当一些消息真正的被执行的时候,callback这个Runnbale方法将会被触发执行的。
接下来有一系列的方法的逻辑是差不多的,我们取其中的一个进行讲解:
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的时候,顺便可以为这个Message提供一些逻辑上需要的参数。
消息有创建,那么就必然存在回收的概念,下面我们一起来看一下:
public
void
recycle() {
clearForRecycle();
synchronized
(sPoolSync) {
if
(sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool =
this
;
sPoolSize++;
}
}
}
void
clearForRecycle() {
flags =
0
;
what =
0
;
arg1 =
0
;
arg2 =
0
;
obj =
null
;
replyTo =
null
;
when =
0
;
target =
null
;
callback =
null
;
data =
null
;
}
在clearForRecycle这个函数中,是做一些回收的预处理的操作,该置为0的参数置为0,该置为null的参数置为null。在recycle的函数中,只要当前的缓存的队列的长度没有超过上限,将当前的消息添加到队列的尾部。
下面的方法是关于实现Parcelable所需要的方法:
public
static
final
Parcelable.Creator<CustomMessage> CREATOR
=
new
Parcelable.Creator<CustomMessage>() {
public
Message createFromParcel(Parcel source) {
Message msg = Message.obtain();
msg.readFromParcel(source);
return
msg;
}
public
Message[] newArray(
int
size) {
return
new
Message[size];
}
};
public
int
describeContents() {
return
0
;
}
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);
}
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);
}
Ok,Message的内核源码的剖析就讲解到这里,相信大家以后再用到这个类的时候会有更深的理解啦。
转载请注明:Android开发中文站 » 详细讲解Android中的Message的源码
Recommend
-
64
前言 本文主要从webpack4.x入手,会对平时常用的Webpack配置一一讲解,各个功能点都有对应的详细例子,所以本文也比较长,但如果你能动手跟着本文中的例子完整写一次,相信你会觉得Webpack也不过如此。 一、什么是webpack,为什么
-
45
上一篇文章 详细讲解 | 利用python开发Burp Suite插件(一) 简单介绍了如何配置burpsuite插件开发环境和burp suite插件api的大概用途,详细内容可参考官方手册。今天将利用python实战写...
-
13
Promise入门到精通(初级篇)-附代码详细讲解 Promise ,中文翻译为 承诺,约定,契约 ,从字面意思来看,这应该是类似某种协议,规定了什么事件发生的条件和触发方法。 ...
-
11
前端开发:详细讲解this指向的相关知识 - 三掌柜666的个人空间 - OSCHINA - 中文开源技术交流社区 ...
-
12
SpringMVC入门详细讲解 看过请留个言,转载请注明出...
-
4
7000字详细讲解区块链的革新者:以太坊 链集市ChainMarket 刚刚 编者注:时间倒退到2013年,进入区块链世界不久后的程序员V...
-
9
什么是vuexVuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。初学vuex,读完这段官方定义以后,黑人问号,感...
-
8
-
9
在前端开发中,this虽然只是其中的一个关键字而已,但是确实一个比较特殊的关键字。为什么说this在js关键字中很特殊呢,原因就在于this是函数在运行时自动生成的一个对象...
-
6
关于vuex,笔者之前写过一篇文章。链接附上:https://segmentfault.com/a/11...vue实例bus事件vue实例bus其实不仅仅可以用在祖孙组件间通信,也可...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK