Intent数据会作为Parcel被存储在Binder事务缓冲区中的对象进行传输。Parcel作为Android底层IPC通信的基础,熟悉Parcel作为了解Binder的第一步。

Parcel在Binder通信中的传输图 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}
obtain中四个值的分布图 obtain中四个值的分布图

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类型的数据存储。

writeInt(2022)中四个值的分布图 writeInt(2022)中四个值的分布图

writeString(“MyParcel”)

同理可知道writeStringwriteInt类似

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}
writeString("MyParcel")中长度部分四个值的分布图 writeString("MyParcel")中长度部分四个值的分布图

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,不相等,触发大段覆盖逻辑。

writeString("MyParcel")中数据部分四个值的分布图 writeString("MyParcel")中数据部分四个值的分布图

这里用黄红两色标识String类型的数据存储。

writeDouble(2.25)

同理可知道writeDoublewriteInt类似

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}
writeDouble(2.25)中四个值的分布图 writeDouble(2.25)中四个值的分布图

这里用蓝色标识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。

readStringreadDouble也是如此,这里不在展开详细解释了。

可以发现读取也是一段连续的空间,并且对存储的空间划分区域取出。这就是为什么写入之后,必须也按顺序读取的原因。

Parcel源码分析原理图 Parcel源码分析原理图

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写入很多信息,包括动作,数据,类型,认证等

AMS中写入的数据结构图 AMS中写入的数据结构图

这里只对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}
Bundle的数据结构图 Bundle的数据结构图

传入到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写入分析。

实例的Bundle的数据结构图 实例的Bundle的数据结构图

Parcelable写入的流程如下图所示。

Parcelable写入的流程图 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}

读取流程如下图所示。

Parcelable读取流程图 Parcelable读取流程图

补充:

Q:关于Bundle中的mMap是哪里来的?

A:来自readArrayMapInternal中的outVal

Bundle中的mMap是继承自BaseBundle,BaseBundle中的mMap通过readArrayMapInternal传入空的outval。

Parcel通过mParcelableData的数据,读取key和value拼接放置到outVal中。其中value是通过Parcel读取readParcel,通过反射真正读取到Bean的三种类型数据。如下图所示。

mMap数据跟踪图 mMap数据跟踪图

总结:

1.Parcel

实际上我们调用的java层的Parcel不是真正的实现,通过jni获取到的mNativePtr共享内存最终调用到Binder下面的Parcel.cpp。

这个cpp中真正意义上的实现并且可以写入读取一段连续的内存,因此我们需要在读取按照写入的顺序依次读取。

2.Parcelable

  • unparcel反序列化过程,将二进制的连续内存转化存入为mMap中(mParcelableData—>mMap
  • parcel是二进制序列化过程,将mMap中的数据转化为二进制的连续内存(mMap—>mParcelableData

Parcel不仅仅是序列化,确切是二进制的序列化。因为序列化的种类很多,ParcelSerializable是二进制序列化。反序列化过程,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}

猜你想看

bytebuffer使用

java序列化

jni6 手写Parcel的C++层及其使用

特别感谢

微博用户S小吠提供的冰墩墩的壁纸

参考

[1] 韦东锏, Parcel的源码上手, 2021.

[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.