android的Parcelable

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 MBSerializable 膨胀后极易 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 实现步骤(通用模板)

  1. 当前类实现 Parcelable
  2. writeToParcel按相同顺序 写出所有字段:
    • 基本类型 → 直接 writeXxx()
    • 字符串 → writeString()
    • 子对象已实现 ParcelablewriteParcelable()
    • 列表/数组 → writeTypedList() / writeTypedArray()
    • Map/其他集合 → 先写 size(),再循环写 key-value
  3. 在构造函数或 createFromParcel按同样顺序读回。
  4. 子对象/集合元素必须 自身也实现 Parcelable,否则编译期不报错,运行期抛 RuntimeException
  5. 生成 CREATOR
  6. 建议用 @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 实例非线程安全,多线程需各自创建

下面给出 MapSet 的完整 Parcelable 手写示例,照抄即可运行。

要点只有两步:

  1. 写:先写 size(),再循环写 key → value (Map)或 元素(Set)。
  2. 读:先读 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/readValueput
Set size → 循环 write元素;读 size → 循环 read元素add

无论多复杂,只要集合元素自身已实现 Parcelable,照此模板就能安全通过 Intent/Bundle 传输。

相关推荐
Kapaseker1 天前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴1 天前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭1 天前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab1 天前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe2 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农2 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少2 天前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker2 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋2 天前
Android 协程时代,Handler 应该退休了吗?
android
火柴就是我2 天前
让我们实现一个更好看的内部阴影按钮
android·flutter