关于Parcel

时间:2024-02-29 20:01:56

关于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;
}