1. Parcelable 是什么
在 Android 中,Parcelable
是一种接口(interface) ,用于实现高效的对象序列化与反序列化 ,以便在不同组件(如 Activity、Service、Binder 等)之间通过 Intent 或 Binder 传递复杂对象。
✅ 1.1为什么需要 Parcelable?
Android 是基于 Binder IPC(进程间通信)机制 的,而 Binder 只支持传输实现了 Parcelable
接口的对象。相比 Java 标准序列化(Serializable
),Parcelable
性能更高、开销更小,是 Android 推荐的序列化方式。
✅ 1.2实现 Parcelable 的基本步骤
1. 实现 Parcelable
接口
java
public class User implements Parcelable {
private int id;
private String name;
// 构造方法
public User(int id, String name) {
this.id = id;
this.name = name;
}
// 从 Parcel 中反序列化
protected User(Parcel in) {
id = in.readInt();
name = in.readString();
}
// 序列化写入 Parcel
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
// 描述内容(通常返回 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];
}
};
}
✅ 1.3使用场景
场景 | 示例 |
---|---|
通过 Intent 传递对象 | intent.putExtra("user", user) |
通过 Bundle 传递 | bundle.putParcelable("key", obj) |
AIDL 接口参数 | Binder 通信中传递对象 |
✅ 1.4Parcelable vs Serializable
特性 | Parcelable | Serializable |
---|---|---|
性能 | ✅ 高效(手动控制) | ❌ 慢(反射机制) |
实现复杂度 | ❌ 稍复杂 | ✅ 简单(只需实现接口) |
推荐场景 | ✅ Android 内组件通信 | ❌ 不建议在 Android 使用 |
✅ 1.5 总结一句话
Parcelable
是 Android 专为高性能 IPC 设计的序列化机制,用于在组件间传递对象 ,比Serializable
更快,但实现稍微繁琐。
Serializable
在 Android 里并不是"不能用",而是**"不推荐"**。原因可以归结为一句话:
它为了通用性牺牲了性能,而 Android 对性能极度敏感。
2. Serializable为什么不建议在 Android 使用
🔍 2.1具体原因拆解
问题点 | 说明 |
---|---|
反射开销大 | 序列化/反序列化全程靠 JVM 反射机制,频繁 IO 与 GC,耗时是 Parcelable 的 5~10 倍。 |
产生大量临时对象 | 反射过程中会创建大量中间对象,触发 GC 抖动,导致卡顿。 |
磁盘/内存额外占用 | 每个对象都要写类描述信息、字段签名等,体积比 Parcelable 大 2~3 倍。 |
无法手动优化 | 字段读写顺序、类型全由 JVM 决定,开发者无法干预。 |
Binder 限制 | Android 的 Binder 一次事务缓冲区只有 1 MB ,Serializable 膨胀后极易 TransactionTooLargeException。 |
📊 2.2官方数据对比(Google IO 演讲)
测试场景 | Serializable 耗时 | Parcelable 耗时 | 倍数 |
---|---|---|---|
1 000 次对象跨进程传递 | 130 ms | 14 ms | 9.3× |
单对象字节大小 | 约 200 B | 约 60 B | 3.3× |
✅ 2.3结论
- 桌面/后台系统 :
Serializable
简单、通用,完全够用。 - Android 应用 :主线程、Binder、内存、电量都是稀缺资源,用 Parcelable 是底线。
除非只是临时调试、原型验证,否则不要在 Android 正式代码里用 Serializable 做 IPC。
3. 复杂对象怎么用Parcelable
在 Android 中,如果对象结构复杂(如含 List、Map、嵌套对象、继承链等),仍可通过 Parcelable
高效传输,但要**"拆到基本类型、按顺序读写、递归处理"**。下面给出完整思路与示例。
3.1 实现步骤(通用模板)
- 当前类实现
Parcelable
。 - 在
writeToParcel
中按相同顺序 写出所有字段:- 基本类型 → 直接
writeXxx()
- 字符串 →
writeString()
- 子对象已实现
Parcelable
→writeParcelable()
- 列表/数组 →
writeTypedList()
/writeTypedArray()
- Map/其他集合 → 先写
size()
,再循环写key-value
。
- 基本类型 → 直接
- 在构造函数或
createFromParcel
中按同样顺序读回。 - 子对象/集合元素必须 自身也实现
Parcelable
,否则编译期不报错,运行期抛RuntimeException
。 - 生成
CREATOR
。 - 建议用
@Parcelize
(Kotlin)或 IDE 插件自动生成,减少手写。
3.2 复杂对象示例(Java)
1. 子对象
java
public class Tag implements Parcelable {
private int id;
private String name;
protected Tag(Parcel in) {
id = in.readInt();
name = in.readString();
}
@Override public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
public static final Creator<Tag> CREATOR = new Creator<Tag>() {
public Tag createFromParcel(Parcel in) { return new Tag(in); }
public Tag[] newArray(int size) { return new Tag[size]; }
};
@Override public int describeContents() { return 0; }
}
2. 主对象(含 List + 嵌套对象)
java
public class Article implements Parcelable {
private long articleId;
private String title;
private ArrayList<Tag> tags; // 复杂1:List
private Author author; // 复杂2:子对象
/* ======== Parcelable ======== */
protected Article(Parcel in) {
articleId = in.readLong();
title = in.readString();
tags = in.createTypedArrayList(Tag.CREATOR); // 读List
author = in.readParcelable(Author.class.getClassLoader()); // 读子对象
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(articleId);
dest.writeString(title);
dest.writeTypedList(tags); // 写List
dest.writeParcelable(author, flags); // 写子对象
}
public static final Creator<Article> CREATOR = new Creator<Article>() {
public Article createFromParcel(Parcel in) { return new Article(in); }
public Article[] newArray(int size) { return new Article[size]; }
};
@Override public int describeContents() { return 0; }
}
3. 使用
java
Intent i = new Intent(context, DetailActivity.class);
i.putExtra("article", article); // article 已实现 Parcelable
startActivity(i);
// 接收端
Article article = getIntent().getParcelableExtra("article");
3.3 Kotlin 极简写法(@Parcelize)
kotlin
@Parcelize
data class Tag(val id: Int, val name: String) : Parcelable
@Parcelize
data class Author(val uid: Long, val nick: String) : Parcelable
@Parcelize
data class Article(
val articleId: Long,
val title: String,
val tags: List<Tag>,
val author: Author
) : Parcelable
编译器自动完成 writeToParcel
/ CREATOR
等模板代码,无需手写 。
3.4 常见坑 & 优化技巧
场景 | 做法 |
---|---|
列表元素未实现 Parcelable | 编译通过,运行抛异常;保证元素类也实现接口 |
Map / Set | 先写 size() ,再循环 writeKey/writeValue ;读回时先读 size() 再 for 循环 |
继承链 | 子类 writeToParcel 先调 super.writeToParcel ,再写自己字段;读时同样先 super |
大数据 | 超过 1 MB 考虑只传 ID 或重新拉取,避免 TransactionTooLargeException |
线程安全 | Parcel 实例非线程安全,多线程需各自创建 |
下面给出 Map 与 Set 的完整 Parcelable
手写示例,照抄即可运行。
要点只有两步:
- 写:先写
size()
,再循环写 key → value (Map)或 元素(Set)。 - 读:先读
size()
,再循环读回来并重新put/add
。
3.5 Map/Set的例子
一、Map 示例
以 Map<String, Score>
为例,其中 Score
是自定义可 Parcelable
的对象。
1. 子对象 Score
java
public class Score implements Parcelable {
private int value;
private String grade;
protected Score(Parcel in) {
value = in.readInt();
grade = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(value);
dest.writeString(grade);
}
public static final Creator<Score> CREATOR = new Creator<Score>() {
public Score createFromParcel(Parcel in) { return new Score(in); }
public Score[] newArray(int size) { return new Score[size]; }
};
@Override public int describeContents() { return 0; }
}
2. 主对象 Student(含 Map)
java
public class Student implements Parcelable {
private String name;
private Map<String, Score> courseScore; // key=课程名, value=分数对象
/* ========== Parcelable ========== */
protected Student(Parcel in) {
name = in.readString();
int size = in.readInt(); // 1. 读size
courseScore = new LinkedHashMap<>(size);
for (int i = 0; i < size; i++) {
String key = in.readString(); // 2. 读key
Score value = in.readParcelable(Score.class.getClassLoader()); // 读value
courseScore.put(key, value);
}
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(courseScore.size()); // 1. 写size
for (Map.Entry<String, Score> entry : courseScore.entrySet()) {
dest.writeString(entry.getKey()); // 2. 写key
dest.writeParcelable(entry.getValue(), flags); // 写value
}
}
public static final Creator<Student> CREATOR = new Creator<Student>() {
public Student createFromParcel(Parcel in) { return new Student(in); }
public Student[] newArray(int size) { return new Student[size]; }
};
@Override public int describeContents() { return 0; }
}
3. 使用
java
Intent i = new Intent(context, DetailActivity.class);
i.putExtra("stu", student); // student 已实现 Parcelable
startActivity(i);
二、Set 示例
以 Set<Permission>
为例,其中 Permission
是可 Parcelable
的枚举式对象。
1. 子对象 Permission
java
public class Permission implements Parcelable {
private String code;
protected Permission(Parcel in) { code = in.readString(); }
@Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(code); }
public static final Creator<Permission> CREATOR = new Creator<Permission>() {
public Permission createFromParcel(Parcel in) { return new Permission(in); }
public Permission[] newArray(int size) { return new Permission[size]; }
};
@Override public int describeContents() { return 0; }
}
2. 主对象 Role(含 Set)
java
public class Role implements Parcelable {
private String roleName;
private Set<Permission> permissions;
protected Role(Parcel in) {
roleName = in.readString();
int size = in.readInt(); // 1. 读size
permissions = new LinkedHashSet<>(size);
for (int i = 0; i < size; i++) {
Permission p = in.readParcelable(Permission.class.getClassLoader());
permissions.add(p); // 2. 读元素并add
}
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(roleName);
dest.writeInt(permissions.size()); // 1. 写size
for (Permission p : permissions) {
dest.writeParcelable(p, flags); // 2. 写元素
}
}
public static final Creator<Role> CREATOR = new Creator<Role>() {
public Role createFromParcel(Parcel in) { return new Role(in); }
public Role[] newArray(int size) { return new Role[size]; }
};
@Override public int describeContents() { return 0; }
}
三、复制即可用
场景 | 口诀 |
---|---|
Map | 写 size → 循环 writeKey/writeValue ;读 size → 循环 readKey/readValue 再 put |
Set | 写 size → 循环 write元素 ;读 size → 循环 read元素 再 add |
无论多复杂,只要集合元素自身已实现 Parcelable
,照此模板就能安全通过 Intent
/Bundle
传输。