Parcel1 Parcel和Parcelable源码分析
3100 Words|Read in about 15 Min|本文总阅读量次
Intent数据会作为Parcel被存储在Binder事务缓冲区中的对象进行传输。Parcel作为Android底层IPC通信的基础,熟悉Parcel作为了解Binder的第一步。
(上图:Android通信过程,其中绿色为相同进程的内部通信,红色为不同进程的跨进程通信,即Binder通信)
Parcel源码分析
如果数据传递是基本类型,直接使用Parcel
传递。其中在App中的Parcel
只是一个幌子,具体是通过native指针的共享内存来调用最终的实现。关于Parcel
的使用,请移步到手写Parcel的C++层及其使用。这里使用的是SDK API30,Android11源码分析。不同版本源码部分会有所区别,但原理是不变的。
首先,对App的Parcel.java接口分为三部分(基本类型、泛型、二进制的序列化)
基本类型 | 基本类型的数组 |
---|---|
writeInt(int) |
writeIntArray(int[]) |
writeFloat(float) |
writeFloatArray(float[]) |
writeDouble(double) |
writeDoubleArray(double[]) |
writeLong(long) |
writeLongArray(long[]) |
writeString(String) |
writeStringArray(String[]) |
writeChar(char) |
writeCharArray(char[]) |
writeByte(byte) |
writeByteArray(byte[]) |
writeBoolean(boolean) |
writeBooleanArray(boolean[]) |
writeValue(Object) |
writeValueArray(Object[]) |
泛型 | 泛型数组 |
writeList(List) |
writeTypedList(List<T>) |
writeMap(Map) |
writeArrayMap(ArrayMap<String, Object>) |
二进制的序列化 | 二进制的序列化数组 |
writeParcelable(Parcelable, int) |
writeParcelableArray(T[], int) |
writeSerializable(Serializable) |
/ |
1.首先获取Parcel
1//Parcel.java#obtain
2//这里面的sOwnedPool跟Message中的类似,这里判断有使用的就用使用过的,都没使用过再实例化一个新的
3public static Parcel obtain() {
4 final Parcel[] pool = sOwnedPool;
5 synchronized (pool) {
6 Parcel p;
7 for (int i=0; i<POOL_SIZE; i++) {
8 p = pool[i];
9 if (p != null) {
10 pool[i] = null;
11 if (DEBUG_RECYCLE) {
12 p.mStack = new RuntimeException();
13 }
14 p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
15 return p;
16 }
17 }
18 }
19 //第一次创建会走到这里
20 return new Parcel(0);
21}
可以看到实例化之后,传入一个0的参数,这个参数实际上是C++端共享内存的地址初始化
1//Parcel.java#Parcel
2private Parcel(long nativePtr) {
3 if (DEBUG_RECYCLE) {
4 mStack = new RuntimeException();
5 }
6 init(nativePtr);
7}
Parcel继续走到init
中
1//Parcel.java#init
2//如果已经存在mNativePtr且不为0,那么继续使用原先的
3//这里是第一次初始化,所以会调用nativeCreate,这个是走到jni层了
4private long mNativePtr; // used by native code
5
6private void init(long nativePtr) {
7 if (nativePtr != 0) {
8 mNativePtr = nativePtr;
9 mOwnsNativeParcelObject = false;
10 } else {
11 mNativePtr = nativeCreate();
12 mOwnsNativeParcelObject = true;
13 }
14}
15
16private static native long nativeCreate();
jni这边会调用到android_os_Parcel.cpp,这个就是设计模式中的外观者模式,真正实现的是Binder中的Parcel.cpp。并且在jni会返回一个实例化对象的地址,正好对应传入的mNativePtr
。
1//frameworks/base/core/jni/android_os_Parcel.cpp#android_os_Parcel_create
2//动态注册省略
3static const JNINativeMethod gParcelMethods[] = {
4 {"nativeCreate", "()J", (void*)android_os_Parcel_create},
5};
6
7static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz)
8{
9 //这个Parcel是在Binder目录下面的Parcel.cpp
10 Parcel* parcel = new Parcel();
11 return reinterpret_cast<jlong>(parcel);
12}
下面真正的Parcel实例化的实现
特别是mData
,mDataSize
,mDataCapacity
,mDataPos
这四个值,以下简称四个值
1//frameworks/native/libs/binder/Parcel.cpp#Parcel、initState
2//下面不仅是一个简单的构造,还初始化一些值,后面write和read会用到
3//特别是mData,mDataSize,mDataCapacity,mDataPos这四个值
4Parcel::Parcel()
5{
6 LOG_ALLOC("Parcel %p: constructing", this);
7 initState();
8}
9
10void Parcel::initState()
11{
12 LOG_ALLOC("Parcel %p: initState", this);
13 mError = NO_ERROR;
14 mData = nullptr;
15 mDataSize = 0;
16 mDataCapacity = 0;
17 mDataPos = 0;
18 mOwner = nullptr;
19 ...
20}
2.Parcel的写入
java层调用writeInt(2022)/writeString(“MyParcel”)/writeDouble(2.25)
接下来的序列化都是采用大端模式。
writeInt(2022)
1//Parcel.java#writeInt
2public final void writeInt(int val) {
3 nativeWriteInt(mNativePtr, val);
4}
5
6@FastNative
7private static native void nativeWriteInt(long nativePtr, int val);
这里直接调用到native层,可以发现这里用到了初始化的mNativePtr
使用
@FastNative
注解可实现对 Java 原生接口 (JNI) 更快速的原生调用。@FastNative
注解支持非静态方法。如果某种方法将jobject
作为参数或返回值进行访问,请使用此注解。@FastNative
可以使原生方法的性能提升高达 3 倍。
1//frameworks/base/core/jni/android_os_Parcel.cpp#android_os_Parcel_writeInt
2//动态注册省略
3static const JNINativeMethod gParcelMethods[] = {
4// @FastNative
5{"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},
6};
7
8static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
9 Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
10 if (parcel != NULL) {
11 const status_t err = parcel->writeInt32(val);
12 if (err != NO_ERROR) {
13 signalExceptionForError(env, clazz, err);
14 }
15 }
16}
调用到Parcel.cpp中的writeInt32
,最终调用到模板类
1//frameworks/native/libs/binder/Parcel.cpp#writeInt32、writeAligned
2status_t Parcel::writeInt32(int32_t val)
3{
4 return writeAligned(val);
5}
6
7//初次进入模板类的流程如下
8//1.判断当前的mDataPos和传入大小是否是比已存在的容量小
9//2.扩容当前的容量
10//3.扩容完成之后,跳转到restart_write中写入,进入finishWrite对数据移位
11template<class T>
12status_t Parcel::writeAligned(T val) {
13 COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
14 //首先判断
15 if ((mDataPos+sizeof(val)) <= mDataCapacity) {
16restart_write:
17 *reinterpret_cast<T*>(mData+mDataPos) = val;
18 return finishWrite(sizeof(val));
19 }
20 //开始扩容
21 status_t err = growData(sizeof(val));
22 if (err == NO_ERROR) goto restart_write;
23 return err;
24}
这个是用于判断扩容的函数
1//frameworks/native/libs/binder/Parcel.cpp#growData
2//这里的#define SIZE_MAX ((size_t)(-1))
3//可以得知扩容方式newSize = ((mDataSize+len)*3)/2;
4status_t Parcel::growData(size_t len)
5{
6 if (len > INT32_MAX) {
7 return BAD_VALUE;
8 }
9
10 if (len > SIZE_MAX - mDataSize) return NO_MEMORY; // overflow
11 if (mDataSize + len > SIZE_MAX / 3) return NO_MEMORY; // overflow
12 size_t newSize = ((mDataSize+len)*3)/2;
13 return (newSize <= mDataSize)
14 ? (status_t) NO_MEMORY
15 : continueWrite(newSize);
16}
设置了扩容方式之后,开始分配内存
1//frameworks/native/libs/binder/Parcel.cpp#continueWrite
2//这里分配内存分两种情况
3//1.如果从未扩容,直接malloc分配内存,并对四个值赋值
4//2.如果本身存在数据,那么就realloc方式扩容,并对四个值赋值
5status_t Parcel::continueWrite(size_t desired)
6{
7 ...
8 else if (mData) {
9 ...
10 //如果本身存在数据,那么就realloc方式扩容,并对四个值赋值
11 if (desired > mDataCapacity) {
12 uint8_t* data = (uint8_t*)realloc(mData, desired);
13 if (data) {
14 pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
15 gParcelGlobalAllocSize += desired;
16 gParcelGlobalAllocSize -= mDataCapacity;
17 pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
18 mData = data;
19 mDataCapacity = desired;
20 }
21 }
22 ...
23 } else {
24 //如果从未扩容,第一次会进入这里,直接malloc分配内存,并对四个值赋值
25 uint8_t* data = (uint8_t*)malloc(desired);
26 if (!data) {
27 mError = NO_MEMORY;
28 return NO_MEMORY;
29 }
30 pthread_mutex_lock(&gParcelGlobalAllocSizeLock);
31 gParcelGlobalAllocSize += desired;
32 gParcelGlobalAllocCount++;
33 pthread_mutex_unlock(&gParcelGlobalAllocSizeLock);
34
35 mData = data;
36 mDataSize = mDataPos = 0;
37 mDataCapacity = desired;
38 }
39
40 return NO_ERROR;
41}
扩容完成写入数据之后,进行数据移位
1//frameworks/native/libs/binder/Parcel.cpp#finishWrite
2//可以发现这里的逻辑跟bytebuffer原理类似,都是通过mData,mDataPos,mDataSize,mDataCapacity操作的
3status_t Parcel::finishWrite(size_t len)
4{
5 if (len > INT32_MAX) {
6 return BAD_VALUE;
7 }
8
9 //这里开始数据移位
10 mDataPos += len;
11 if (mDataPos > mDataSize) {
12 mDataSize = mDataPos;
13 }
14 return NO_ERROR;
15}
这里用绿色标识Int
类型的数据存储。
writeString(“MyParcel”)
同理可知道writeString
跟writeInt
类似
1//Parcel.java#writeString
2public final void writeString(@Nullable String val) {
3 writeString16(val);
4}
这个String的写入比较特殊,会经过mReadWriteHelper
来转一下,最终调用writeString16NoHelper
1//Parcel.java#writeString16NoHelper
2public void writeString16NoHelper(@Nullable String val) {
3 nativeWriteString16(mNativePtr, val);
4}
5
6@FastNative
7private static native void nativeWriteString16(long nativePtr, String val);
这里直接调用到native层,可以发现这里也用到了初始化的mNativePtr
这里有一个GetStringCritical
,得到的是一个指向JVM内部字符串的直接指针,提高JVM返回源字符串直接指针的可能性,具体可看这里。
1//frameworks/base/core/jni/android_os_Parcel.cpp#android_os_Parcel_writeString16
2//这里需要传入两个值,一个为字符串的地址,一个是字符串的length长度
3//动态注册省略
4static const JNINativeMethod gParcelMethods[] = {
5// @FastNative
6 {"nativeWriteString16", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString16},
7}
8
9static void android_os_Parcel_writeString16(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val)
10{
11 Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
12 if (parcel != NULL) {
13 status_t err = NO_MEMORY;
14 if (val) {
15 //通过val,获取到str
16 const jchar* str = env->GetStringCritical(val, 0);
17 if (str) {
18 //传入两个值,一个值为str字符串,另一个为string内容的length
19 err = parcel->writeString16(
20 reinterpret_cast<const char16_t*>(str),
21 env->GetStringLength(val));
22 env->ReleaseStringCritical(val, str);
23 }
24 } else {
25 err = parcel->writeString16(NULL, 0);
26 }
27 if (err != NO_ERROR) {
28 signalExceptionForError(env, clazz, err);
29 }
30 }
31}
调用到writeString16
1//frameworks/native/libs/binder/Parcel.cpp#writeString16
2//writeString写入分成两个部分
3//1.写入String类型的长度,这个跟上述writeInt(2022)完全一致
4//2.写入String类型的内容(考虑到String需要最后加‘\0’,且为String16类型,单个字符占两个字节)
5status_t Parcel::writeString16(const char16_t* str, size_t len)
6{
7 if (str == nullptr) return writeInt32(-1);
8 //1.writeInt跟上述一致
9 status_t err = writeInt32(len);
10 if (err == NO_ERROR) {
11 //2.len的大小为原来的两倍
12 len *= sizeof(char16_t);
13 uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
14 if (data) {
15 memcpy(data, str, len);
16 //将返回已分配好内存的地址返回,字符串最后设置为‘\0’
17 //这个设置0的原因,writeInplace中paded分配长度可能会大于传入的len,触发大段覆盖逻辑
18 *reinterpret_cast<char16_t*>(data+len) = 0;
19 return NO_ERROR;
20 }
21 err = mError;
22 }
23 return err;
24}
writeInt
这里不再阐述,直接看writeInplace
1//frameworks/native/libs/binder/Parcel.cpp#writeInplace
2//这里的len为字符串的(length+1)*2
3//这里的String用到了计算所需长度特殊的函数pad_size
4void* Parcel::writeInplace(size_t len)
5{
6 //通过paded方式计算所需空间
7 const size_t padded = pad_size(len);
8 if (mDataPos+padded < mDataPos) {
9 return nullptr;
10 }
11
12 if ((mDataPos+padded) <= mDataCapacity) {
13restart_write:
14 uint8_t* const data = mData+mDataPos;
15 //大段覆盖逻辑:判断paded的长度和传入的len长度,如果不相等,那么最后四个字节就按照打断方式赋值
16 if (padded != len) {
17 //这里定义了大端和小端,我们用的是大端
18#if BYTE_ORDER == BIG_ENDIAN
19 static const uint32_t mask[4] = {
20 0x00000000, 0xffffff00, 0xffff0000, 0xff000000
21 };
22#endif
23#if BYTE_ORDER == LITTLE_ENDIAN
24 static const uint32_t mask[4] = {
25 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff
26 };
27#endif
28 *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
29 }
30
31 finishWrite(padded);
32 return data;
33 }
34 status_t err = growData(padded);
35 if (err == NO_ERROR) goto restart_write;
36 return nullptr;
37}
这里还有一个padded
的长度,这个是writeString
里面独有的
1//frameworks/native/libs/binder/Parcel.cpp#pad_size
2#define PAD_SIZE_UNSAFE(s) (((s)+3)&~3)
3
4static size_t pad_size(size_t s) {
5 if (s > (std::numeric_limits<size_t>::max() - 3)) {
6 LOG_ALWAYS_FATAL("pad size too big %zu", s);
7 }
8 return PAD_SIZE_UNSAFE(s);
9}
可以知道,传入的“MyParcel”,paded
大小为20,len
为18,不相等,触发大段覆盖逻辑。
这里用黄红两色标识String
类型的数据存储。
writeDouble(2.25)
同理可知道writeDouble
跟writeInt
类似
1//Parcel.java#writeDouble
2public final void writeDouble(double val) {
3 nativeWriteDouble(mNativePtr, val);
4}
5
6@FastNative
7private static native void nativeWriteDouble(long nativePtr, double val);
这里直接调用到native层,可以发现这里也用到了初始化的mNativePtr
1//frameworks/base/core/jni/android_os_Parcel.cpp#android_os_Parcel_writeDouble
2//动态注册省略
3static const JNINativeMethod gParcelMethods[] = {
4// @FastNative
5 {"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble},
6}
7
8static void android_os_Parcel_writeDouble(JNIEnv* env, jclass clazz, jlong nativePtr, jdouble val)
9{
10 Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
11 if (parcel != NULL) {
12 const status_t err = parcel->writeDouble(val);
13 if (err != NO_ERROR) {
14 signalExceptionForError(env, clazz, err);
15 }
16 }
17}
调用到writeDouble
。显然这个和writeInt类似,都进入到模板函数中
1//frameworks/native/libs/binder/Parcel.cpp#pad_size
2status_t Parcel::writeDouble(double val)
3{
4 return writeAligned(val);
5}
这里用蓝色标识Double
类型的数据存储。
可以发现三次write
操作,实际上存储是一段连续的空间,并且对空间的数进行划分存储。
3.Parcel的读出
java层调用readInt()/readString()/readDouble
readInt()
1//Parcel.java#readInt
2public final int readInt() {
3 return nativeReadInt(mNativePtr);
4}
5
6@CriticalNative
7private static native int nativeReadInt(long nativePtr);
用初始化的mNativePtr
调用nativeReadInt
1//frameworks/base/core/jni/android_os_Parcel.cpp#android_os_Parcel_readInt
2//动态注册省略
3static const JNINativeMethod gParcelMethods[] = {
4// @CriticalNative
5 {"nativeReadInt", "(J)I", (void*)android_os_Parcel_readInt},
6};
7
8static jint android_os_Parcel_readInt(jlong nativePtr)
9{
10 Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
11 if (parcel != NULL) {
12 return parcel->readInt32();
13 }
14 return 0;
15}
跟writeInt32
同理,最终readInt32
调用到模板函数readAligned
中
1//frameworks/native/libs/binder/Parcel.cpp#readInt32、readAligned
2int32_t Parcel::readInt32() const
3{
4 return readAligned<int32_t>();
5}
6
7template<class T>
8T Parcel::readAligned() const {
9 T result;
10 if (readAligned(&result) != NO_ERROR) {
11 result = 0;
12 }
13
14 return result;
15}
16
17template<class T>
18status_t Parcel::readAligned(T *pArg) const {
19 COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
20
21 if ((mDataPos+sizeof(T)) <= mDataSize) {
22 if (mObjectsSize > 0) {
23 //这个是在内存中的数据有效性校验,如果传入的值跟内存的所需长度不一致就会异常
24 status_t err = validateReadData(mDataPos + sizeof(T));
25 if(err != NO_ERROR) {
26 // Still increment the data position by the expected length
27 mDataPos += sizeof(T);
28 return err;
29 }
30 }
31 //这里开始读取数据
32 const void* data = mData+mDataPos;
33 mDataPos += sizeof(T);
34 *pArg = *reinterpret_cast<const T*>(data);
35 return NO_ERROR;
36 } else {
37 return NOT_ENOUGH_DATA;
38 }
39}
但是发现如果在write之后直接read,pos和data所指的位置在最后,是不正确的。
所需在write和read之间必须调用setDataPosition(0);
,让其重置到数据的第一位。
1//Parcel.java#setDataPosition
2public final void setDataPosition(int pos) {
3 nativeSetDataPosition(mNativePtr, pos);
4}
5
6@CriticalNative
7private static native void nativeSetDataPosition(long nativePtr, int pos);
用初始化的mNativePtr
调用nativeReadInt
1//frameworks/base/core/jni/android_os_Parcel.cpp#android_os_Parcel_setDataPosition
2//动态注册省略
3static const JNINativeMethod gParcelMethods[] = {
4// @CriticalNative
5 {"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition},
6}
7
8static void android_os_Parcel_setDataPosition(jlong nativePtr, jint pos)
9{
10 Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
11 if (parcel != NULL) {
12 parcel->setDataPosition(pos);
13 }
14}
可以看到最终使得pos重置为0
1//frameworks/native/libs/binder/Parcel.cpp#setDataPosition
2void Parcel::setDataPosition(size_t pos) const
3{
4 if (pos > INT32_MAX) {
5 // don't accept size_t values which may have come from an
6 // inadvertent conversion from a negative int.
7 LOG_ALWAYS_FATAL("pos too big: %zu", pos);
8 }
9
10 mDataPos = pos;
11 mNextObjectHint = 0;
12 mObjectsSorted = false;
13}
重置完成之后,开始读取Int值就正常了。数值为2022。
readString
和readDouble
也是如此,这里不在展开详细解释了。
可以发现读取也是一段连续的空间,并且对存储的空间划分区域取出。这就是为什么写入之后,必须也按顺序读取的原因。
Parcelable.java源码分析
如果传递的不是基本类型是对象,那么需要用到的是Parcelable
的实现类。
然而Parcelable.java最终也是通过Parcel.java来实现的。
下面通过简单的两个Activity
页面的跳转Intent
传递的Bundle
值来说明Parcalable
原理,具体请移步到手写Parcel的C++层及其使用。
数据写入
1.putExtra
这里直接从Activity
中调用putExtra
1//MainActivity.java#onCreate
2//我们从这里开始调用
3intent.putExtra("P1", bean1);//这里的bean1的javaBean为(2022,"MyParcel",2.25)
4intent.putExtra("P2", bean2);//这里的bean2的javaBean为(2022,"AndroidSourceCode",2.25)
调用到Intent
中的putExtra
1//Intent.java#putExtra
2//可以看到最终调用Bundle里面的putParcelable方法
3public @NonNull Intent putExtra(String name, @Nullable Parcelable value) {
4 if (mExtras == null) {
5 mExtras = new Bundle();
6 }
7 mExtras.putParcelable(name, value);
8 return this;
9}
很显然,传递进去的值都是通过Bundle
来传递的
1//Bundle.java#putParcelable
2//这里面有两个比较重要的一个是unparcel
3//这个unparcel先按下不表,作用是mParcelledData这个Parcel中解包,把数据取出存入mMap中
4//另一个是mMap,这个用于将刚刚放入Bundle中的数据,通过键值对存到mMap中
5public void putParcelable(@Nullable String key, @Nullable Parcelable value) {
6 unparcel();
7 mMap.put(key, value);
8 mFlags &= ~FLAG_HAS_FDS_KNOWN;
9}
上面的流程是将数据存入到Bundle
中的mMap
中。
2.AMS的回调
在Activity启动过程中,由AMS完成进程通信,期间,将调用**Intent.writeToParcel()**将所有必要数据进行序列化,并完成传输。(具体AMS如何调到Intent的writeToParcel,接下来的AMS篇章会解释)
1//Intent.java#writeToParcel
2public void writeToParcel(Parcel out, int flags) {
3 out.writeString8(mAction);
4 Uri.writeToParcel(out, mData);
5 out.writeString8(mType);
6 out.writeString8(mIdentifier);
7 out.writeInt(mFlags);
8 out.writeString8(mPackage);
9 ComponentName.writeToParcel(mComponent, out);
10
11 if (mSourceBounds != null) {
12 out.writeInt(1);
13 mSourceBounds.writeToParcel(out, flags);
14 } else {
15 out.writeInt(0);
16 }
17
18 if (mCategories != null) {
19 final int N = mCategories.size();
20 out.writeInt(N);
21 for (int i=0; i<N; i++) {
22 out.writeString8(mCategories.valueAt(i));
23 }
24 } else {
25 out.writeInt(0);
26 }
27
28 if (mSelector != null) {
29 out.writeInt(1);
30 mSelector.writeToParcel(out, flags);
31 } else {
32 out.writeInt(0);
33 }
34
35 if (mClipData != null) {
36 out.writeInt(1);
37 mClipData.writeToParcel(out, flags);
38 } else {
39 out.writeInt(0);
40 }
41 out.writeInt(mContentUserHint);
42 out.writeBundle(mExtras);
43}
可以看到实际上通过Parcel写入很多信息,包括动作,数据,类型,认证等
这里只对Bundle进行展开分析,其他的感兴趣的宝宝可以自助分析
1//Bundle.java#writeBundle、writeToParcel
2public final void writeBundle(@Nullable Bundle val) {
3 if (val == null) {
4 writeInt(-1);
5 return;
6 }
7 val.writeToParcel(this, 0);
8}
9
10@Override
11public void writeToParcel(Parcel parcel, int flags) {
12 final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
13 try {
14 super.writeToParcelInner(parcel, flags);
15 } finally {
16 parcel.restoreAllowFds(oldAllowFds);
17 }
18}
最终调用到了Bundle
基类中的writeToParcelInner
1//BaseBundle.java#writeToParcelInner
2//这个方法内容比较多,拆分成几部分
3//1.unparcel,先按下不表,数据压入到mMap
4//2.第一次进入mParcelledData没有数据,直接往下走不退出,且将unparcel中的mMap数据给到map
5//3.不是第一次进入,mParcelledData有数据,Parcel数据拼接给mParcelledData
6void writeToParcelInner(Parcel parcel, int flags) {
7 if (parcel.hasReadWriteHelper()) {
8 unparcel();
9 }
10 final ArrayMap<String, Object> map;
11 synchronized (this) {
12 if (mParcelledData != null) {
13 if (mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL) {
14 parcel.writeInt(0);
15 } else {
16 //mParcelledData有数据,Parcel数据拼接给mParcelledData
17 int length = mParcelledData.dataSize();
18 parcel.writeInt(length);
19 parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
20 parcel.appendFrom(mParcelledData, 0, length);
21 }
22 return;
23 }
24 //第一次进入mParcelledData没有数据,直接往下走不退出
25 map = mMap;
26 }
27
28 if (map == null || map.size() <= 0) {
29 parcel.writeInt(0);
30 return;
31 }
32 //开始写入一些信息,-1(map的长度),魔数和map数据
33 int lengthPos = parcel.dataPosition();
34 parcel.writeInt(-1); // dummy, will hold length
35 parcel.writeInt(BUNDLE_MAGIC);
36
37 int startPos = parcel.dataPosition();
38 parcel.writeArrayMapInternal(map);
39 int endPos = parcel.dataPosition();
40 //写完map数据后,再将原来是-1的值改成map的长度
41 parcel.setDataPosition(lengthPos);
42 int length = endPos - startPos;
43 parcel.writeInt(length);
44 parcel.setDataPosition(endPos);
45}
然后对map具体进一步写入分析,把写入的每一步细化出来
1//Parcel.java#writeArrayMapInternal、writeValue
2void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) {
3 if (val == null) {
4 writeInt(-1);
5 return;
6 }
7 //统计val的map中有多少个元素,然后循环遍历key和value
8 final int N = val.size();
9 writeInt(N);
10 int startPos;
11 for (int i=0; i<N; i++) {
12 if (DEBUG_ARRAY_MAP) startPos = dataPosition();
13 writeString(val.keyAt(i));
14 writeValue(val.valueAt(i));
15 }
16}
17
18public final void writeValue(@Nullable Object v) {
19 if (v == null) {
20 writeInt(VAL_NULL);
21 }
22 ...
23 else if (v instanceof Parcelable) {
24 //写入value的类型
25 writeInt(VAL_PARCELABLE);
26 writeParcelable((Parcelable) v, 0);
27 }
28 ...
29}
传入到v
,实际上就是实现Parcelable
的Bean类的对象
1//Parcel.java#writeParcelable、writeParcelableCreator
2public final void writeParcelable(@Nullable Parcelable p, int parcelableFlags) {
3 if (p == null) {
4 writeString(null);
5 return;
6 }
7 writeParcelableCreator(p);
8 p.writeToParcel(this, parcelableFlags);
9}
10
11public final void writeParcelableCreator(@NonNull Parcelable p) {
12 //写入全类名
13 String name = p.getClass().getName();
14 writeString(name);
15}
最终调用到了Bean类中的writeToParcel
1//Bean#writeToParcel
2@Override
3public void writeToParcel(Parcel dest, int flags) {
4 Log.d(TAG, "->>>ParcelTest|writeToParcel|dest = " + dest + " flags = " + flags);
5 dest.writeInt(age);
6 dest.writeString(name);
7 dest.writeInt(count);
8}
这里的bean1的javaBean为(2022,"MyParcel",2.25)
和bean2的javaBean为(2022,"AndroidSourceCode",2.25)
那么实际的数为下图所示。绿色代表Int
类型,浅绿色代表Int
的魔数数据,黄红双色为String
类,蓝色代表Double
类型,具体可根据Parcel源码解析结合Parcal写入分析。
Parcelable
写入的流程如下图所示。
数据读出
从新的Activity
中读取
getIntent().getParcelableExtra("P1");
和getIntent().getParcelableExtra("P2");
1//Intent.java#getParcelableExtra
2public @Nullable <T extends Parcelable> T getParcelableExtra(String name) {
3 return mExtras == null ? null : mExtras.<T>getParcelable(name);
4}
很显然,先前传递进去的值都是通过Bundle
来获取的
1//Bundle.java
2//这里分成两部分
3//1.unparcel,这个是读取过程中最重要的,将数据放入到mMap中
4//2.通过mMap中的key获取对应value的Parcelable数据
5@Nullable
6public <T extends Parcelable> T getParcelable(@Nullable String key) {
7 unparcel();
8 Object o = mMap.get(key);
9 if (o == null) {
10 return null;
11 }
12 try {
13 return (T) o;
14 } catch (ClassCastException e) {
15 typeWarning(key, o, "Parcelable", e);
16 return null;
17 }
18}
主要分析这个unparcel
1//BaseBundle.java#unparcel
2//显然是通过mParcelledData数据来处理这个过程,这个数据在Bundle基类中的writeToParcelInner也存在
3@UnsupportedAppUsage
4/* package */ void unparcel() {
5 synchronized (this) {
6 final Parcel source = mParcelledData;
7 if (source != null) {
8 initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
9 } else {
10 if (DEBUG) {
11 Log.d(TAG, "unparcel "
12 + Integer.toHexString(System.identityHashCode(this))
13 + ": no parcelled data");
14 }
15 }
16 }
17}
通过传入的mParcelledData
来分析这个读取过程
1//BaseBundle.java
2//这个内容比较多,分成几个部分
3//1.parcelledData的有效性判断
4//2.清空mMap数据,并开始重新写入
5//3.ParcelData的读取Map内容过程,将数据读取到map中,赋值给mMap
6private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
7 boolean parcelledByNative) {
8 ...
9 if (isEmptyParcel(parcelledData)) {
10 ...
11 if (mMap == null) {
12 mMap = new ArrayMap<>(1);
13 } else {
14 mMap.erase();
15 }
16 mParcelledData = null;
17 mParcelledByNative = false;
18 return;
19 }
20 //先读取二进制中存入的map的size
21 final int count = parcelledData.readInt();
22 ...
23 if (count < 0) {
24 return;
25 }
26 //这里操作为清空mMap数据,并开始重新写入
27 ArrayMap<String, Object> map = mMap;
28 if (map == null) {
29 map = new ArrayMap<>(count);
30 } else {
31 map.erase();
32 map.ensureCapacity(count);
33 }
34
35 try {
36 if (parcelledByNative) {
37 parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
38 } else {
39 //这个是ParcelData的读取Map内容过程
40 parcelledData.readArrayMapInternal(map, count, mClassLoader);
41 }
42 } catch (BadParcelableException e) {
43 if (sShouldDefuse) {
44 Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
45 map.erase();
46 } else {
47 throw e;
48 }
49 } finally {
50 //读取中的数据,重新写入到空的mMap中
51 mMap = map;
52 //这个是用来将上述的parcelledData保存到Parcel的sOwnedPool中
53 if (recycleParcel) {
54 recycleParcel(parcelledData);
55 }
56 mParcelledData = null;
57 mParcelledByNative = false;
58 }
59 ...
60}
读取parcelledData
中的数据,这里传入的map
是空的
1//Parcel.java#readArrayMapInternal
2//这里是真正意义上读取二进制的map数据中的key和value
3void readArrayMapInternal(@NonNull ArrayMap outVal, int N,
4 @Nullable ClassLoader loader) {
5 int startPos;
6 while (N > 0) {
7 if (DEBUG_ARRAY_MAP) startPos = dataPosition();
8 String key = readString();
9 Object value = readValue(loader);
10 //开始通过读取key,value保存到outVal中
11 outVal.append(key, value);
12 N--;
13 }
14 outVal.validate();
15}
对readValue
进一步分析,这个value
实际就是Parcelable
1//Parcel.java#readValue
2@Nullable
3public final Object readValue(@Nullable ClassLoader loader) {
4 int type = readInt();
5
6 switch (type) {
7 case VAL_NULL:
8 return null;
9
10 case VAL_PARCELABLE:
11 return readParcelable(loader);
12 ...
13}
这里对Parcelable
读取操作
1//Parcel.java#readParcelable
2public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
3 //通过类加载器获取到Parcelable实现的Bean类中的creator
4 Parcelable.Creator<?> creator = readParcelableCreator(loader);
5 if (creator == null) {
6 return null;
7 }
8 if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
9 Parcelable.ClassLoaderCreator<?> classLoaderCreator =
10 (Parcelable.ClassLoaderCreator<?>) creator;
11 return (T) classLoaderCreator.createFromParcel(this, loader);
12 }
13 //调用到Bean内部类的createFromParcel
14 return (T) creator.createFromParcel(this);
15}
这边会使用到一次java反射,并对传入的类加载器进入处理
1//Parcel.java#readParcelableCreator
2//第一次进入做了一次反射操作,获取creator
3//1.第一次进去,map中通过全类名获取不到对应的Bean的creator,反射获取,存入到map中
4//2.非第一次进入,通过全类名可以获取到对应的Bean的creator
5@Nullable
6public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
7 //获取全类名
8 String name = readString();
9 if (name == null) {
10 return null;
11 }
12 Parcelable.Creator<?> creator;
13 HashMap<String, Parcelable.Creator<?>> map;
14 synchronized (mCreators) {
15 map = mCreators.get(loader);
16 if (map == null) {
17 map = new HashMap<>();
18 mCreators.put(loader, map);
19 }
20 creator = map.get(name);
21 }
22 //非第一次进入,通过在map中全类名获取到具体的creator
23 if (creator != null) {
24 return creator;
25 }
26 //第一次进入获取不到具体的creator
27 try {
28 ClassLoader parcelableClassLoader =
29 (loader == null ? getClass().getClassLoader() : loader);
30 //通过全类名,类加载器,获取对应的Class对象,即Bean对象
31 Class<?> parcelableClass = Class.forName(name, false /* initialize */,
32 parcelableClassLoader);
33 if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
34 throw new BadParcelableException("Parcelable protocol requires subclassing "
35 + "from Parcelable on class " + name);
36 }
37 //获取Bean对象中的静态内部类CREATOR字段信息
38 Field f = parcelableClass.getField("CREATOR");
39 //getModifiers的解释详见如下
40 if ((f.getModifiers() & Modifier.STATIC) == 0) {
41 throw new BadParcelableException("Parcelable protocol requires "
42 + "the CREATOR object to be static on class " + name);
43 }
44 //获取静态内部类的类型,并判断是否和Bean中的类型一致
45 Class<?> creatorType = f.getType();
46 if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
47 throw new BadParcelableException("Parcelable protocol requires a "
48 + "Parcelable.Creator object called "
49 + "CREATOR on class " + name);
50 }
51 //获取静态对象的Field属性值,静态方法可以传递null
52 creator = (Parcelable.Creator<?>) f.get(null);
53 } catch (IllegalAccessException e) {
54 Log.e(TAG, "Illegal access when unmarshalling: " + name, e);
55 throw new BadParcelableException(
56 "IllegalAccessException when unmarshalling: " + name);
57 } catch (ClassNotFoundException e) {
58 Log.e(TAG, "Class not found when unmarshalling: " + name, e);
59 throw new BadParcelableException(
60 "ClassNotFoundException when unmarshalling: " + name);
61 } catch (NoSuchFieldException e) {
62 throw new BadParcelableException("Parcelable protocol requires a "
63 + "Parcelable.Creator object called "
64 + "CREATOR on class " + name);
65 }
66 if (creator == null) {
67 throw new BadParcelableException("Parcelable protocol requires a "
68 + "non-null Parcelable.Creator object called "
69 + "CREATOR on class " + name);
70 }
71 //得到所想要的creator之后,将其存入值map中,下一次可以通过全类名去获取到
72 synchronized (mCreators) {
73 map.put(name, creator);
74 }
75
76 return creator;
77}
Java反射机制的常用方法
1.getModifiers
Field的getModifiers()方法返回int类型值表示该字段的修饰符
1PUBLIC: 1 2PRIVATE: 2 3PROTECTED: 4 4STATIC: 8 5FINAL: 16 6SYNCHRONIZED: 32 7VOLATILE: 64 8TRANSIENT: 128 9NATIVE: 256 10INTERFACE: 512 11ABSTRACT: 1024 12STRICT: 2048
2.isAssignableFrom
- isAssignableFrom()方法是从类继承的角度去判断,instanceof关键字是从实例继承的角度去判断。
- isAssignableFrom()方法是判断是否为某个类的父类,instanceof关键字是判断是否某个类的子类。
1父类.class.isAssignableFrom(子类.class) 2 3子类实例 instanceof 父类类型
3.Field.get(null)
关于Field.get(null),详见这里
定义了嵌套接口,那么接口默认修饰为public static
最终调用到了creator.createFromParcel
,这个就是我们实现的Bean
1//Bean.java
2public static final Creator<ParcelTest> CREATOR = new Creator<ParcelTest>() {
3 @Override
4 public ParcelTest createFromParcel(Parcel in) {
5 //最终会调用到这里,这里会调用Parcel的有参构造,完成真正意义的Bean内容读取
6 Log.d(TAG, "->>>ParcelTest|createFromParcel|in = " + in);
7 return new ParcelTest(in);
8 }
9
10 @Override
11 public ParcelTest[] newArray(int size) {
12 Log.d(TAG, "->>>ParcelTest|newArray|size = " + size);
13 return new ParcelTest[size];
14 }
15};
16
17protected ParcelTest(Parcel in) {
18 Log.d(TAG, "->>>ParcelTest|in = " + in);
19 age = in.readInt();
20 name = in.readString();
21 count = in.readInt();
22}
读取流程如下图所示。
补充:
Q:关于
Bundle
中的mMap
是哪里来的?A:来自
readArrayMapInternal
中的outVal
。Bundle中的mMap是继承自BaseBundle,BaseBundle中的mMap通过readArrayMapInternal传入空的outval。
Parcel通过mParcelableData的数据,读取key和value拼接放置到outVal中。其中value是通过Parcel读取readParcel,通过反射真正读取到Bean的三种类型数据。如下图所示。
总结:
1.Parcel
实际上我们调用的java层的Parcel不是真正的实现,通过jni获取到的mNativePtr
共享内存最终调用到Binder
下面的Parcel.cpp。
这个cpp中真正意义上的实现并且可以写入读取一段连续的内存,因此我们需要在读取按照写入的顺序依次读取。
2.Parcelable
unparcel
反序列化过程,将二进制的连续内存转化存入为mMap
中(mParcelableData
—>mMap
)parcel
是二进制序列化过程,将mMap
中的数据转化为二进制的连续内存(mMap
—>mParcelableData
)
Parcel
不仅仅是序列化,确切是二进制的序列化。因为序列化的种类很多,Parcel
和Serializable
是二进制序列化。反序列化过程,java会出现一次反射,序列化过程中不需要反射。
3.嵌套的Bean对象
如果javaBean中对象嵌套了另一个javaBean,如果BeanInnerBean类里面有其他对象(比如实体类DataBean)的话,那么DataBean也需要实现Parcelable接口,用法与上面的Bean类一样。
writeToParcel里面需要写上:
1@Override
2public void writeToParcel(Parcel dest, int flags) {
3 ...
4 dest.writeParcelable(dataBean, 0);
5}
BeanInnerBean构造函数中
1protected BeanInnerBean(Parcel in) {
2 ...
3 dataBean = in.readParcelable(DataBean.class.getClassLoader());
4}
猜你想看
特别感谢
微博用户S小吠提供的冰墩墩的壁纸
参考
[2] MxsQ, Parcelable 是如何实现的?, 2020.
[3] Boyikia, 理解 Parcel 和 Parcelable, 2019.
[4] 铁憨憨的学习记录, Java反射机制getModifiers()方法的作用, 2018.
[5] zero9988, 简要分析Android中的Intent,Bundle,Parcel中的数据传递, 2017.
[6] freshui, Parcel数据传输过程,简要分析Binder流程, 2017.
[7] 水蓝城城主, java.nio.ByteBuffer用法小结, 2019.
[8] zhangbijun1230, Android 8.0学习(31)—Android 8.0 中的 ART 功能改进, 2018.
[9] xyang0917, JNI/NDK开发指南(四)——字符串处理, 2014.
[10] 茅坤宝骏氹, java反射的field.get(null), 2018.
[11] wkCaeser_, java中isAssignableFrom()方法与instanceof关键字用法及通过反射配合注解为字段设置默认值, 2018.