关于Parcel
1. 概念:
Parcel是一个容器,他可以包含数据或者是对象引用,并且能够用于Binder的传输。同时支持序列化以及跨进程之后进行反序列化。在android系统中用途非常广泛,主要功能就是用来进行IPC的通信,用来序列化的Parcelable接口,还有aidl其实也就是封装了Parcel的数据传递。
2. 数据结构:
Parcel底层维护了一个用来存储数据的变量:
uint8_t* mData;
对应Java层的是marshall方法返回的结果。
以及一个‘游标’,用来指示数据的位置:
mutable size_t mDataPos;
uint8 其实就是一个unsigned char的数据类型。写进去的数据大小取决于数据类型,比如以基本数据类型为例,int,float是4个字节,long是8个字节,boolean其实是写int,也占4个字节,走的都是一个泛型函数,字符串不一样,用张图来描述:
如果字符串是空,则写入writeInt(-1),代表无值。其他的数据类型比如数组,binder对象等,差不多都是像字符串的写入过程。反过来读数据也是差不多这么个过程,所以我们可以看到,Parcel本地写入数据的过程必须得和读数据的顺序是一样的,如果中间多或少了一个数据,或者读多读少一个数据,后面的数据就会全部打乱,导致数据都是错的(这种方法有点像对称加密)。
3.应用
四大组件都是用parcel从本地向远处服务AMS传递相关的数据,下面是startService的过程(代码来源: ActivityManagerNative.java , 7.1版本),理论上,我们在native层只要按照顺序写入我们自己的数据,提交给远程系统服务,也一样可以启动我们的service,这样还可以缩短时间。
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
service.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeString(callingPackage);
data.writeInt(userId);
mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
ComponentName res = ComponentName.readFromParcel(reply);
data.recycle();
reply.recycle();
return res;
}