携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
前言
这里是IPC的系列,为什么突然要说序列化呢?首先我们需要知道什么是序列化,以及各种序列化的区别。
序列化就是把对象转成易于传输和保存的过程,注意这里的关键字是传输和保存,而对于传输我们又分为俩种:
- 一种是网络传输,比如在后台把一个Book对象经过序列化,经过互联网传输到手机,手机进行展示时,这时就需要反序列化才可以使用和展示。
这种情况下,我们一般采用序列化的方式是把对象转成Json格式,这种序列化的优点是序列化后的内容不包含序列化之前类的信息,而且数据量较小。
- 另一种是对于Android开发来说,我们页面跳转需要使用Intent,可以用Intent传递数据,比如从A Activity跳转到B Activity需要传递一个Book对象,这时可以通过Serializable或者Parcelable进行序列化,来进行传输。
还有一点就是保存,对于保存来说,我们既可以序列化成Json格式,也可以通过Serializable序列化为字节格式,这个不是本章的重点。
本章的重点我们聚焦于使用Parcelable和Serializable实现的序列化方式,而对于使用fastJson或者Gson等库的其他序列化方式不是本章说明点。
正文
我们平时在APP中启动Activity时,经常有如下代码:
findViewById<Button>(R.id.jump1).setOnClickListener {
startActivity(Intent(this, SecondActivity::class.java).apply {
//put一个Parcelable对象
putExtra("key",aTest)
//put一个Serializable对象
putExtra("key1",BTest())
})
}
在这里我们可以使用Intent传递一个对象,这个对象必须是序列化过的,要不是通过Java的Serializable接口,要不是Android独有的Parcelable接口,我们就来自己探究一下这俩种序列化方式的实现和区别。
Serializable接口
Serializable是Java提供的一个序列化接口,它是一个空接口,对对象提供标准的序列化和反序列化操作。使用起来非常方便,只需要实现该接口,同时在类中声明一个如下的标识符:
class BTest : Serializable {
companion object{
private const val serialVersionUID = 1L
}
var test: String? = null
}
其中serialVersionUID是可以省略的。
如何序列化
还是回到文章最开始的地方,序列化的作用是为了方便传输和保存,而Serializable提供的序列化就是推荐用来持久化保存,这里就需要用到ObjectOutputStream和ObjectInputStream,常见的使用代码如下:
val bTest = BTest().apply {
test = "okk"
}
val file = File(Environment.getExternalStorageDirectory().path, "cache1.txt")
if (!file.exists()){
file.createNewFile()
}
//序列化
val out = ObjectOutputStream(FileOutputStream(file))
out.writeObject(bTest)
out.close()
//反序列化
val inStream = ObjectInputStream(FileInputStream(file))
val b = inStream.readObject() as BTest
上述代码就是使用Serializable接口进行序列化持久化保存和反序列的过程,我们简单来看一下它的writeObject方法:
public final void writeObject(Object obj) throws IOException {
...
try {
writeObject0(obj, false);
} catch (IOException ex) {
...
throw ex;
}
}
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
...
//使用反射
Object orig = obj;
Class<?> cl = obj.getClass();
ObjectStreamClass desc;
...
//按类型进行读写
if (obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
// END Android-changed: Make Class and ObjectStreamClass replaceable.
} else if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
//会判断是否是Serializable类型
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
...
try {
desc.checkSerialize();
//进行IO操作
bout.writeByte(TC_OBJECT);
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
通过上面代码我们可以知道如下实现细节:
- 会使用反射,在writeObject方法中会通过反射来拿到类的成员信息。众所周知,反射是一个比较耗时的操作。
- 会固定判断几种类型,比如String,Enum等,其中最主要的就是会判断Serializable类型,这也就说明了要使用ObjectOutputStream进行序列化对象时,该对象必须是Serializable类型。
- 在真正序列化时,把元素保存为字节时,需要用到IO操作,而IO操作也是一种比较费时的操作。
从上面3点分析,我们也看出了使用Serializable的弊端,即效率较差。
serialVersionUID用处
在前面序列化类时,使用了serialVersionUID这个字段,它是一长串数字,一般情况下,我们都会选择忽略,但是这个字段是有意义的,我们要正确理解其含义,可以防止一些问题出现。
serialVersionUID是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才可以正常被反序列化。
它的工作机制是这样的:序列化时,系统会把当前类的serialVersionUID写入序列化文件中,当反序列化时,会检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致则说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功序列化;否则就说明当前类和序列化类相比发生了变换,比如成员变量的数量、类型可能发生改变,这时就无法反序列化。
一般来说,我们应该手动去指定serialVersionUID的值,比如1L这种,或者根据当前类的结构自动去生成一个hash值。这样可以防止一种崩溃,就是序列化的类当增加或者减少了成员变量,如果不指定serialVersionUID的值,Serializable会在序列化和反序列化时通过hash对类计算出值,这时就无法正常反序列化,如果指定了serialVersionUID值,还是可以正常反序列化的。当然如果类的结构发生了毁灭性变化,比如成员变量的类型发生了变化,这样即使指定了serialVersionUID也无法还原。
这里再注意俩点:
- 静态成员变量属于类不属于对象,所以不会参与序列化过程。
- 用transient关键字标记的成员变量不会参与序列化过程。
会保存类的信息
这个是啥意思呢? 不论是Serializable还是Parcelable进行序列化的结果都会保存原始类的信息,这个特性也就导致了这俩种序列化方式不适合用在网络传输中,因为谁也无法保证在服务器和客户端定义的类是一样的。
比如下面2个类:
class BTest : Serializable {
var test: String? = null
}
class BTest1 : Serializable {
var test: String? = null
}
他们的成员变量名以及类型是一样的,但是类名不一样,当我们把BTest对象进行序列化后,用BTest1类型进行反序列化时会报错,如果我们用Gson给序列化为Json字符串就不会出现这种情况,所以不同的序列化方式要灵活选择使用。
Parcelable
Parcelable作为Android独有的序列化接口,不知道你是否有没有想过,为什么Android要单独设计出这种序列化方式,毕竟已经有了Java的Serializable接口,而且还可以把类序列化为json字符串来保存和传递也非常方便,这些问题我们接下来一一分析。
使用
还是先来看看如何使用Parcelable,还是实现Parcelable接口,但是实现的内容比较多,代码如下:
public class Book implements Parcelable {
public int BookId;
public String BookName;
public Book(int bookId,String bookName){
this.BookId = bookId;
this.BookName = bookName;
}
protected Book(Parcel in) {
BookId = in.readInt();
BookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(BookId);
dest.writeString(BookName);
}
}
和使用Serializable的简洁不同,实现了Parcelable接口多了很多代码,不过这些代码都是可以自动生成,无需手动写。
序列化不外乎就是序列化和反序列化,所以上述代码通过writeToParcel实现了序列化,而通过一个静态内部类Creator实现了反序列化,从实现细节来看序列化和反序列化就是往Parcel中写数据和读数据的过程。
下面是几个方法梳理:
方法 | 功能 |
---|---|
writeToParcel | 将当前对象写入序列化结构中,即把对象写入Parcel中 |
createFromParcel | 从序列化的对象中创建原始对象,即从Parcel中读取数据 |
newArray | 创建指定长度的原始对象数组,可以不用在意 |
Book(Parcel in) | 从parcel对象中创建原始对象 |
describeContents | 返回当前对象的内容描述,默认为0 |
好了,到现在我们会发现Parcelable的核心就是Parcel了,搞明白这个Parcel就能整明白为什么Android要单独搞一个序列化接口了。
Parcel原理
其实Parcel的翻译为"包裹"、"包"的意思,所以Parcel的设计初衷我觉得并不是序列化,它只是数据的包裹类,而该包裹类可以通过IBinder传输。
同时Parcel并不是一个通用的序列化机制,是不推荐使用Parcelable来进行数据持久化保存的。
上面是Parcel的官方注释,其中有个非常重要的点是Parcel包裹的数据可以在IBinder之间传输,关于IBinder接口可以做个简单介绍,它是通过Binder进行IPC必须的接口,所以Parcel是为了高性能的IPC通信而设计的。
既然这里说了Parcel比较高效,我们就来看看其如何实现,下面是源码部分:
//Book类中
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(BookId);
dest.writeString(BookName);
}
//写入一个int值到parcel中
public final void writeInt(int val) {
int err = nativeWriteInt(mNativePtr, val);
if (err != OK) {
nativeSignalExceptionForError(err);
}
}
//会调用Native层方法
private static native int nativeWriteInt(long nativePtr, int val);
这里会调用底层C++代码,在android_os_Parcel.cpp中实现如下:
{"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
const status_t err = parcel->writeInt32(val);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
会发现它会调用C++的Parcel对象的方法:
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
//这个就是C++层核心代码,写入数据val
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
//其中mData就是parcel申请的共享内存
//mDataPos就是这块共享内存的地址
//这里先计算mDataPos加val大小是否超过容量,如果超过容量会去扩容,再重新赋值
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
//计算mData加mDataPos得到物理地址,转成T类型的指针,然后将val赋值给指针指向的内容
restart_write:
*reinterpret_cast<T*>(mData+mDataPos) = val;
return finishWrite(sizeof(val));
}
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
由上面代码分析可知在C++层,会将数据写入到一块共享的内存中,所以同理我们从Parcel中读取数据时,也是从这块共享内存中读取。
这时不了解Linux进程的同学,会有点疑惑这个共享的内存是啥玩意。其实这个在后面我们说内存映射时,会好好介绍,这里可以先做个简单了解。
我们一个APP的进程所运行的内存空间是分为用户空间和内核空间的,其中不同进程的用户空间不共享,但是内核空间是共享的;用户空间和内核空间交互时需要通过系统调用,这时为了防止用户空间内的代码发生问题导致系统一块挂掉。
而Parcel把数据保存的地方就是共享空间的内存,所以它和前面所说的Serializable区别不同,Serializable是通过IO操作来保存数据,而Parcel则是直接对内存操作,所以速度比Serializable快很多。
还有一点注意就是,因为Parcel保存是按内存地址加偏移量来保存的,所以读数据时,成员变量的顺序不能乱,一定要和保存时保持一致。
其他补充
上面我们对俩种序列化方式做了原理性的探究,透过现象看本质后,我们发现这俩种区别还是非常大的。
大致总结如下:
- 因为Serializable需要频繁的IO操作,且涉及到反射调用,而Parcelable只是对共享内存的操作,所以Parcelable比Serializable要快很多。
- Parcelable实现比较复杂,且需要读写顺序一致性,Serializable则只需要继承接口即可,比较方便。
- 俩者的序列化结果都会保留原类信息,和Json的序列化格式有本质区别。
- Parcelable的设计初衷就是高效的IPC通信,所以在Android中使用内存序列化时(比如Intent使用)使用Parcelable,而做数据持久化使用Serializable。
本来这篇文章到此就结束了,但是对于Intent可以传递Parcelable和Serializable数据我有点疑惑,因为Parcelable接口的实现是有代码的,但是实现Serializable接口则是一个空实现,那是什么时候去调用前面所说的ObjectOutputStream和ObjectInputStream呢 我们就来简单看一下流程。
在MainActivity中,我们跳转其他Activity,并且携带一个Serializable对象:
startActivity(Intent(this, SecondActivity::class.java).apply {
//put一个Serializable对象
putExtra("key",aTest)
})
//Intent中方法
public @NonNull Intent putExtra(String name, @Nullable Serializable value) {
if (mExtras == null) {
mExtras = new Bundle();
}
//可以发现这里其实是mExtras才是真正携带数据的
mExtras.putSerializable(name, value);
return this;
}
//mExtras是Bundle类型
private Bundle mExtras;
//put一个Serializable数据就是往mMap中塞数据
void putSerializable(@Nullable String key, @Nullable Serializable value) {
unparcel();
mMap.put(key, value);
}
//map是一个ArrayMap
ArrayMap<String, Object> mMap = null;
通过上面代码我们可以知道真实携带数据就是Bundle,我们看一下Bundle类定义:
public final class Bundle extends BaseBundle implements Cloneable, Parcelable
可以发现这个类居然也是实现了Parcelable接口,接下来我们分析当页面跳转时,参数是如何传递的,这部分涉及Activity的启动,前面的流程就不说了,我们直接从ActivityManagerNative调用Intent的writeToParcel说起:
public void writeToParcel(Parcel out, int flags) {
//调用Parcel的writeBundle方法
out.writeBundle(mExtras);
}
public final void writeBundle(@Nullable Bundle val) {
if (val == null) {
writeInt(-1);
return;
}
//调用Bundle的writeToParcel方法
val.writeToParcel(this, 0);
}
//这里就是把Bundle的内容写入一个Parcel中,为了可以在Binder中通信
public void writeToParcel(Parcel parcel, int flags) {
final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
try {
//调用父类的方法
super.writeToParcelInner(parcel, flags);
} finally {
parcel.restoreAllowFds(oldAllowFds);
}
}
void writeToParcelInner(Parcel parcel, int flags) {
...
int lengthPos = parcel.dataPosition();
//这里先写入一些开始标志符号
//这里的writeXXX函数在前面我们分析过了
parcel.writeInt(-1); // placeholder, will hold length
parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
//真正开始写入Bundle数据
parcel.writeArrayMapInternal(map);
int endPos = parcel.dataPosition();
//写入数据长度,以及设置结束位置
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
}
所以代码回到Parcel中:
void writeArrayMapInternal(@Nullable ArrayMap<String, Object> val) {
...
//先写入数据长度
final int N = val.size();
writeInt(N);
...
int startPos;
//遍历Bundle
for (int i=0; i<N; i++) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
//写入key
writeString(val.keyAt(i));
//写入值
writeValue(val.valueAt(i));
if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + " "
+ (dataPosition()-startPos) + " bytes: key=0x"
+ Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0)
+ " " + val.keyAt(i));
}
}
到这里我们逐渐清晰,还记得我们最开始传递的Serializable对象吗,它将会在writeValue中写入:
//判断数据类型,针对不同的数据类型,采用不同的写入方法,即把不同的object都展开为数据
//写入到parcel中
public final void writeValue(@Nullable Object v) {
if (v == null) {
writeInt(VAL_NULL);
} else if (v instanceof String) {
writeInt(VAL_STRING);
writeString((String) v);
} else if (v instanceof Integer) {
writeInt(VAL_INTEGER);
writeInt((Integer) v);
} else if (v instanceof Map) {
writeInt(VAL_MAP);
writeMap((Map) v);
} else if (v instanceof Bundle) {
// Must be before Parcelable
writeInt(VAL_BUNDLE);
writeBundle((Bundle) v);
} else if (v instanceof PersistableBundle) {
writeInt(VAL_PERSISTABLEBUNDLE);
writePersistableBundle((PersistableBundle) v);
} else if (v instanceof Parcelable) {
// IMPOTANT: cases for classes that implement Parcelable must
// come before the Parcelable case, so that their specific VAL_*
// types will be written.
writeInt(VAL_PARCELABLE);
writeParcelable((Parcelable) v, 0);
} else if (v instanceof Short) {
writeInt(VAL_SHORT);
writeInt(((Short) v).intValue());
} else if (v instanceof Long) {
writeInt(VAL_LONG);
writeLong((Long) v);
} else if (v instanceof Float) {
writeInt(VAL_FLOAT);
writeFloat((Float) v);
} else if (v instanceof Double) {
writeInt(VAL_DOUBLE);
writeDouble((Double) v);
} else if (v instanceof Boolean) {
writeInt(VAL_BOOLEAN);
writeInt((Boolean) v ? 1 : 0);
} else if (v instanceof CharSequence) {
// Must be after String
writeInt(VAL_CHARSEQUENCE);
writeCharSequence((CharSequence) v);
} else if (v instanceof List) {
writeInt(VAL_LIST);
writeList((List) v);
} else if (v instanceof SparseArray) {
writeInt(VAL_SPARSEARRAY);
writeSparseArray((SparseArray) v);
} else if (v instanceof boolean[]) {
writeInt(VAL_BOOLEANARRAY);
writeBooleanArray((boolean[]) v);
} else if (v instanceof byte[]) {
writeInt(VAL_BYTEARRAY);
writeByteArray((byte[]) v);
} else if (v instanceof String[]) {
writeInt(VAL_STRINGARRAY);
writeStringArray((String[]) v);
} else if (v instanceof CharSequence[]) {
// Must be after String[] and before Object[]
writeInt(VAL_CHARSEQUENCEARRAY);
writeCharSequenceArray((CharSequence[]) v);
} else if (v instanceof IBinder) {
writeInt(VAL_IBINDER);
writeStrongBinder((IBinder) v);
} else if (v instanceof Parcelable[]) {
writeInt(VAL_PARCELABLEARRAY);
writeParcelableArray((Parcelable[]) v, 0);
} else if (v instanceof int[]) {
writeInt(VAL_INTARRAY);
writeIntArray((int[]) v);
} else if (v instanceof long[]) {
writeInt(VAL_LONGARRAY);
writeLongArray((long[]) v);
} else if (v instanceof Byte) {
writeInt(VAL_BYTE);
writeInt((Byte) v);
} else if (v instanceof Size) {
writeInt(VAL_SIZE);
writeSize((Size) v);
} else if (v instanceof SizeF) {
writeInt(VAL_SIZEF);
writeSizeF((SizeF) v);
} else if (v instanceof double[]) {
writeInt(VAL_DOUBLEARRAY);
writeDoubleArray((double[]) v);
} else {
Class<?> clazz = v.getClass();
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
// Only pure Object[] are written here, Other arrays of non-primitive types are
// handled by serialization as this does not record the component type.
writeInt(VAL_OBJECTARRAY);
writeArray((Object[]) v);
} else if (v instanceof Serializable) {
// Must be last
//必须要放到最后
writeInt(VAL_SERIALIZABLE);
writeSerializable((Serializable) v);
} else {
throw new RuntimeException("Parcel: unable to marshal value " + v);
}
}
}
上面有非常多的类型,里面细节就不说了,其实就对应着Intent可以传递哪些类型的数据类型,其中在最后的地方,终于看到了我们想看到的类型:Serializable,接着看该函数实现:
public final void writeSerializable(@Nullable Serializable s) {
if (s == null) {
writeString(null);
return;
}
String name = s.getClass().getName();
writeString(name);
//字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
//还是通过ObjectOutputStream
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(s);
oos.close();
//写入字节数组
writeByteArray(baos.toByteArray());
} catch (IOException ioe) {
throw new RuntimeException("Parcelable encountered " +
"IOException writing serializable object (name = " + name +
")", ioe);
}
}
好了,到这我们终于发现了即使使用Intent传入了Serializable类型的数据,最后还是通过把Serializable类型数据转成字节数组,通过Parcel写入共享内存,来完成通信。
这里提供一个小技巧,就是这种找代码调用关系比较复杂时,可以手动创建crash,通过Log日志就可以发现,比如上面调用流程当传入的类型不对时,会报下面crash:
//错误栈
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = com.zyh.ipc.ATest)
//报错部分
at android.os.Parcel.writeSerializable(Parcel.java:1527)
at android.os.Parcel.writeValue(Parcel.java:1475)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:724)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408)
at android.os.Bundle.writeToParcel(Bundle.java:1157)
at android.os.Parcel.writeBundle(Parcel.java:764)
at android.content.Intent.writeToParcel(Intent.java:8700)
at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:3082)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1518)
at android.app.Activity.startActivityForResult(Activity.java:4244)
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:597)
at android.app.Activity.startActivityForResult(Activity.java:4202)
at androidx.activity.ComponentActivity.startActivityForResult(ComponentActivity.java:583)
at android.app.Activity.startActivity(Activity.java:4541)
at android.app.Activity.startActivity(Activity.java:4509)
at com.zyh.ipc.MainActivity.onCreate$lambda-2(MainActivity.kt:26)
//APP代码部分
at com.zyh.ipc.MainActivity.$r8$lambda$zUKr2W1DarwwQ9g_Poh-FBVnM4g(MainActivity.kt)
at com.zyh.ipc.MainActivity$$ExternalSyntheticLambda2.onClick(Unknown Source)
at android.view.View.performClick(View.java:5637)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1131)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6145)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:892)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:782)
通过上面的错误调用栈,我们可以清晰地看出调用关系。
总结
关于序列化的知识还是非常多的,首先就是Java提供的Serializable接口,通过查看源码我们发现其内部涉及IO操作和反射调用,所以性能一般;其次就是Android特有的Parcelable接口,其原理就是Parcel包裹类实现,序列化和反序列化过程其实的通过C++代码操作共享内存,所以性能非常好,非常适合IPC通信。
然后对Serializable和Parcelable做了比较,以及通过Intent传递对象数据我们得知数据其实保存在Bundle中,Serializable类型数据也会转成字节数组写入共享内存。
后续文章继续探究IPC其他知识点。
笔者水平有限,如有问题,欢迎大家评论指正。