Java-Parcel序列化
Java Parcel 是 native android::Parcel 的受控包装。Java 对象保存 mNativePtr 和所有权标志,obtain() 从对象池复用包装,读写方法经 JNI 修改 native 数据区与对象表,recycle() 清空 buffer 后把包装放回不同池。回收后的 Parcel 不再属于调用者,即使 Java 引用仍然存在也不能继续使用。
本文承接 Parcel序列化详解、writeStrongBinder与readStrongBinder 和 Java-IBinder接口。本文聚焦 Java 包装、JNI 映射、异常与回收;native 数据区和 offsets 算法不重复展开。
1. 对象所有权
1.1 核心字段
源码文件:frameworks/base/core/java/android/os/Parcel.java
private long mNativePtr;
private boolean mOwnsNativeParcelObject;
private long mNativeSize;
private boolean mRecycled = false;mNativePtr 指向 native Parcel。mOwnsNativeParcelObject 区分 Java 自己分配的 Parcel 与 Binder incoming transaction 临时包装:两者回收时进入不同池,holder 包装不能错误释放不属于自己的 native 对象。
1.2 obtain
public static Parcel obtain() {
Parcel res = null;
synchronized (sPoolSync) {
if (sOwnedPool != null) {
res = sOwnedPool;
sOwnedPool = res.mPoolNext;
sOwnedPoolSize--;
}
}
if (res == null) {
res = new Parcel(0);
} else {
res.mRecycled = false;
res.mReadWriteHelper = ReadWriteHelper.DEFAULT;
}
return res;
}对象池复用的是 Java 包装及其 native owner,不保证保留上一次数据;回收时已经调用 freeBuffer()。obtain(IBinder) 还会 markForBinder(),让 native Parcel 选择 kernel Binder 或 RPC wire context。
1.3 recycle
public final void recycle() {
if (mRecycled) {
throw new IllegalStateException("recycle twice?");
}
mRecycled = true;
freeBuffer();
if (mOwnsNativeParcelObject) {
// return to owned pool
} else {
mNativePtr = 0;
// return to holder pool
}
}重复 recycle 是错误;回收后调用者不得继续读写。owned Parcel 保留 native 对象用于池复用,holder Parcel 清零 native pointer,避免继续访问 Binder 线程提供的临时对象。
2. JNI读写
2.1 基本类型
源码文件:frameworks/base/core/jni/android_os_Parcel.cpp
static int android_os_Parcel_writeInt(
jlong nativePtr, jint val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return parcel != nullptr ? parcel->writeInt32(val) : OK;
}
static jint android_os_Parcel_readInt(jlong nativePtr) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
return parcel != nullptr ? parcel->readInt32() : 0;
}Java writeInt/readInt 直接映射 native aligned int32。Java API 的异常转换由 JNI registration/helper 完成;native null pointer 返回默认值是防御分支,不是合法回收后使用语义。
2.2 字符串
JNI 写 String16 时先写 UTF-16 code unit 长度,再预留 (len + 1) 空间并写终止符。该格式必须与 native Parcel.cpp 保持同步,源码注释明确提示两端不能独立修改。
2.3 数据位置
public final int dataPosition() {
return nativeDataPosition(mNativePtr);
}
public final void setDataPosition(int pos) {
nativeSetDataPosition(mNativePtr, pos);
}Java 位置就是 native mDataPos。写完后读取通常必须重置到 0;Binder transact()/execTransact() 会在边界处处理位置,但手工 Parcel 测试和本地调用仍需明确管理。
3. Binder对象
3.1 写入
static void android_os_Parcel_writeStrongBinder(
JNIEnv* env, jlong nativePtr, jobject object) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != nullptr) {
status_t err = parcel->writeStrongBinder(
ibinderForJavaObject(env, object));
if (err != NO_ERROR) signalExceptionForError(env, err);
}
}ibinderForJavaObject() 区分 Java Binder 与 BinderProxy:前者取得 JavaBBinderExt,后者取得 native BpBinder。之后沿 native Parcel 的 flatten 规则写本地对象或 handle。
3.2 读取
native readStrongBinder() 返回 sp<IBinder>,JNI 再用 javaObjectForIBinder():若是当前 JVM 创建的 JavaBBinderExt,返回原 Java Binder;否则从 BinderProxy.ProxyMap 复用或创建 Java proxy。这保证同一 native Binder 在 Java 层尽量保持对象身份。
4. 异常协议
4.1 写异常
服务端 Binder.execTransactInternal() 捕获同步调用异常时清空 reply,再调用 reply.writeException(e)。Parcel 把异常类型编码为 exception code,并按类型附加 message 或 Parcelable header。
4.2 读异常
客户端 AIDL Proxy 调用 reply.readException(),将 exception code 转回 SecurityException、IllegalArgumentException、ServiceSpecificException 或 RemoteException 等。transport RemoteException 与服务端写入的业务异常不是同一个来源。
4.3 Oneway边界
oneway 没有同步 reply,因此服务端 RuntimeException 只能记录,不能由客户端 readException() 获取。Parcel 异常协议只在有 reply 的调用链生效。
5. Marshall边界
5.1 原始字节
marshall()/unmarshall() 复制 Parcel raw bytes,但文档明确禁止把该格式用于持久化或跨版本存储。Binder 对象和 fd 还依赖对象表/进程资源,不能只保存 byte[] 完整恢复。
5.2 Context隔离
markForBinder() 通过 JNI 把目标 Binder 交给 native Parcel,确定 kernel/RPC 格式。不同 RPC session 或 kernel/RPC Parcel 不能任意 appendFrom()。
6. 测试边界
6.1 ParcelTest
源码文件:frameworks/base/core/tests/coretests/src/android/os/ParcelTest.java
测试使用 obtain()、写入字段、重置 position、读取并在 finally/recycle 中归还 Parcel;Binder 相关测试写入同一 Binder、append 到另一 Parcel,并验证对象身份与数据比较。class cookie 测试还验证 recycle 后不能继续访问。
6.2 Nullability
源码文件:frameworks/base/core/tests/coretests/src/android/os/ParcelNullabilityTest.java
测试写入 null strong Binder 和 nullable 容器,验证 nullable API 与 non-null API 的不同结果。它不覆盖驱动跨进程 node/ref 翻译失败。
6.3 可执行阅读
rg -n "mNativePtr|mOwnsNativeParcelObject|obtain\(|recycle\(|setDataPosition" \
frameworks/base/core/java/android/os/Parcel.java
rg -n "writeInt|readInt|writeStrongBinder|readStrongBinder|markForBinder" \
frameworks/base/core/jni/android_os_Parcel.cpp
rg -n "writeException|readException|writeStrongBinder|recycle" \
frameworks/base/core/tests/coretests/src/android/os/ParcelTest.java \
frameworks/base/core/tests/coretests/src/android/os/ParcelNullabilityTest.java排查 Java Parcel 错误时,先确认对象是否已 recycle、native pointer 的 ownership、data position、读写类型和 Binder context,再区分 JNI/native status、transport RemoteException 与 reply 中编码的业务异常。
