文章目录
-
- 一、为什么需要序列化
- [二、Serializable --- Java 原生方式](#二、Serializable — Java 原生方式)
-
- [2.1 基本用法](#2.1 基本用法)
- [2.2 传递数据](#2.2 传递数据)
- [2.3 原理简析](#2.3 原理简析)
- [三、Parcelable --- Android 专属方案](#三、Parcelable — Android 专属方案)
-
- [3.1 基本用法](#3.1 基本用法)
- [3.2 读取和写入顺序必须一致](#3.2 读取和写入顺序必须一致)
- [3.3 传递数据](#3.3 传递数据)
- [3.4 原理简析](#3.4 原理简析)
- 四、性能对比
- 五、总结
一、为什么需要序列化
Android 中几个典型场景都需要序列化:
- Intent 传递对象 :
intent.putExtra("user", user) - Fragment 传递参数 :通过
Bundle传对象 - 进程间通信(IPC):AIDL 跨进程传递数据
- 状态保存 :
onSaveInstanceState保存页面状态
以上场景中,数据需要被转换成能在组件间传递的二进制流,这个过程就是序列化。
二、Serializable --- Java 原生方式
Serializable 是 Java 标准库自带的接口,使用非常简单,只需在类上实现即可。
2.1 基本用法
java
public class User implements Serializable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// getter / setter 省略...
}
2.2 传递数据
java
// 发送端
User user = new User("张三", 25);
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("user", user);
startActivity(intent);
// 接收端
User user = (User) getIntent().getSerializableExtra("user");
2.3 原理简析
Serializable 底层通过 Java 反射机制 进行序列化:
- 遍历类的所有字段,通过反射读取值
- 将字段名、类型、值写入输出流
- 反序列化时通过反射创建对象,再逐个赋值
因为依赖反射,过程中会产生大量临时对象,频繁触发 GC,性能较差。
三、Parcelable --- Android 专属方案
Parcelable 是 Android SDK 提供的接口,专门为 Android 场景优化。
3.1 基本用法
java
public class User implements Parcelable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// 从 Parcel 中读取数据,构造对象
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
}
// 将对象数据写入 Parcel
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
// 一般返回 0 即可,特殊场景才用文件描述符
@Override
public int describeContents() {
return 0;
}
// 必须有的 CREATOR,用于反序列化时创建对象
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
// getter / setter 省略...
}
3.2 读取和写入顺序必须一致
这是最容易出错的地方:writeToParcel 的写入顺序,必须和构造函数中 readXxx 的读取顺序完全一致。
java
// 正确:写入顺序和读取顺序一致
dest.writeString(name); // 写入 ①
dest.writeInt(age); // 写入 ②
name = in.readString(); // 读取 ①
age = in.readInt(); // 读取 ②
java
// 错误:顺序不一致会导致数据错乱
dest.writeString(name);
dest.writeInt(age);
age = in.readInt(); // 先读了 age,但 Parcel 里排第一的是 String
name = in.readString();
3.3 传递数据
用法和 Serializable 一样,接收端换成 getParcelableExtra:
java
// 发送端
User user = new User("张三", 25);
intent.putExtra("user", user);
// 接收端
User user = getIntent().getParcelableExtra("user");
3.4 原理简析
Parcelable 不依赖反射,而是手动将字段按顺序拆解并写入 Parcel(一块共享内存):
- 你手动指定每个字段如何写入内存
- 序列化后数据存储在
Parcel容器中(底层是 Native 内存) - 反序列化时按写入顺序逐一读取,直接赋值
整个过程不走反射,无临时对象开销,是纯手工的高效方案。
四、性能对比
| 对比维度 | Serializable | Parcelable |
|---|---|---|
| 实现复杂度 | 低,implements 即可 | 高,需手动写大量模板代码 |
| 序列化速度 | 慢(反射 + 大量临时对象) | 快(直接内存读写) |
| 内存占用 | 高(GC 压力大) | 低(共享内存,无额外对象) |
| 跨进程支持 | 支持(Java 标准) | 支持(Android 专属) |
| 持久化存储 | 支持(可存文件/数据库) | 不推荐(版本升级数据不兼容) |
一个具体的对比数据:
序列化 1000 个包含 3 个字段的简单对象,Parcelable 比 Serializable 快 10 倍以上 ,内存占用仅为后者的 1/3。
这也是为什么 Android 官方文档明确推荐:在 Intent / Bundle 传递数据时使用 Parcelable。
五、总结
Serializable 胜在简单,Parcelable 胜在高效。
实际开发中,面向 Intent 传参的场景优先用 Parcelable。如果你觉得手写 Parcelable 太繁琐,可以借助 Android Studio 的插件(如 Parcelable 代码生成器)来一键生成。