Parcelable 原理解析

​   Android实现进程间通讯,首选的数据传递就是Parcelable了,接下来主要讲的就是Parcelable实现数据传递的原理,对于使用,可以直接在网上百度。对于已经使用过Parcelable的,不知道有没有发现,不管是读数据还是写数据,都涉及到一个对象,这个对象就是Parcel,内部数据传递实际使用的就是这个对象,接下来就来看看是如何实现的。

​   要分析Parcel,首先要思考的是该如何下手,Parcel主要是用于进程间通讯,那么首先想到就是aidl了,先来编写一个aidl文件:

interface Library {

String queryBook(in String inString, CustomParcelable cus);

}

接着再来看看生成的对应文件,首先先看下发送数据的地方:

@Override public java.lang.String queryBook(java.lang.String inString, CustomParcelable cus) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(inString);
if ((cus!=null)) {
_data.writeInt(1);
cus.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_queryBook, _data, _reply, 0);
...
}

这里注意_data这个对象,是一个Parcel类型,传进来的inString通过writeString写入到_data中,注意看这个Parcelable类型的数据对象,调用了他的writeToParcel()方法,将_data对象传递进去,注意看重点cus.writeToParcel(_data, 0),这行代码就会执行到实现了Pacelable接口的writeToParcel(_data, 0),有用过Parcelable传递数据的都知道,内部都是在writeToParcel()这个方法中将数据写入到_data中,所以实际处理数据都是通过Parcel来的,Parcel的方法很多,这里选一个很有代表性的方法来说明,这个方法是Parcel的writeParcelable():

public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
if (p == null) {
writeString(null);
return;
}
writeParcelableCreator(p);
p.writeToParcel(this, parcelableFlags);
}

public final void writeParcelableCreator(@NonNull Parcelable p) {
String name = p.getClass().getName();
writeString(name);
}

这里主要做了两步,一是获取当前Parcelable对象的全路径名,并写入到底层,这步的作用主要是为了后面的读取;二是再次调用了Parcelable的writeToParcel(),这其实就是一个递归调用,为的是将Parcelable中的数据全是写入进去,其结果就是最终写入到结果都是一些基本数据类型,这里看下Parcel支持的写的数据:

private static native void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len);
private static native void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len);
@FastNative
private static native void nativeWriteInt(long nativePtr, int val);
@FastNative
private static native void nativeWriteLong(long nativePtr, long val);
@FastNative
private static native void nativeWriteFloat(long nativePtr, float val);
@FastNative
private static native void nativeWriteDouble(long nativePtr, double val);
@FastNative
private static native void nativeWriteString8(long nativePtr, String val);
@FastNative
private static native void nativeWriteString16(long nativePtr, String val);
@FastNative
private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
@FastNative
private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);

看到这里,可能会想,写入都是一些基本数据,那要怎么读取出来并返回到对应的数据对象呢?接下来就来分析下,这里先回到aidl生成的文件当中,aidl接收数据的接口是onTransact()函数,先来看看:

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
...
case TRANSACTION_queryBook:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
CustomParcelable _arg1;
if ((0!=data.readInt())) {
_arg1 = CustomParcelable.CREATOR.createFromParcel(data);
}
...
}
}

这里主要是CustomParcelable.CREATOR.createFromParcel(data),是不是很熟悉,就是Parcelable里的CREATOR,这里的data是从客户端传过来的Parcel对象,客户端也就是通过这个对象写入数据携带过来的,同样,读取数据我们也选取一个具有代表性的方法readParcelable()进行说明:

public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
Parcelable.Creator<?> creator = readParcelableCreator(loader);
if (creator == null) {
return null;
}
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
Parcelable.ClassLoaderCreator<?> classLoaderCreator =
(Parcelable.ClassLoaderCreator<?>) creator;
return (T) classLoaderCreator.createFromParcel(this, loader);
}
return (T) creator.createFromParcel(this);
}

这个方法需要传一个ClassLoader对象进来,这个对象有什么用呢?当然有,这里进入到readParcelableCreator(loader)就明白了:

public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
String name = readString();
...

ClassLoader parcelableClassLoader = (loader == null ? getClass().getClassLoader() : loader);

Class<?> parcelableClass = Class.forName(name, false, parcelableClassLoader);
...
Field f = parcelableClass.getField("CREATOR");
...
creator = (Parcelable.Creator<?>) f.get(null);
...

return creator;
}

代码精简了下,看起来就很明了了,最先开始执行的是readString(),还记得前面在写入Parcelable对象的时候,第一步是去获取Parcelable对象的全路径名,然后写入到Parcel对象中,这里就是将全路径名读取出来,接下来就是通过class路径名反射去获取对象了,这个没什么好说的,这里有个需要注意的地方,就是这个ClassLoader,如果没有传入ClassLoader的话,默认使用的是加载Parcel的加载器,Parcel的加载器是系统加载器,如果这个对象是在我们应用中的定义的类,那么使用系统class加载器是加载不出来的,也就是会返回null,所以这个时候是需要我们传入class加载器的。到这,应该就明白了,传入的这个class加载器是用来反射生成对象的,创建好对象后,接着就调用了Parcelable的createFromParcel()方法,这个方法里就是将客户端写入parcel中的内容按顺序读取出来,Parcel中写入数据的顺序和读取出来的顺序是需要对应的,内部实现是通过指针来记录位置的。

最后再来总结下:

1、Parcelable是通过Parcel来进行写入和读取数据;

2、Parcelable是通过指针来记录位置,写入和读物需要完全对应;

3、Parcelable的CREATOR获取是需要传入ClassLoader进行反射获取拿到的,ClassLoader主要分为两种,一种是系统的ClassLoader,一种是加载应用的ClassLoader,默认使用的是系统的ClassLoader。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值