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 传输。

相关推荐
顾林海2 小时前
Android编译插桩之AspectJ:让代码像特工一样悄悄干活
android·面试·性能优化
叽哥2 小时前
Flutter Riverpod上手指南
android·flutter·ios
循环不息优化不止2 小时前
安卓开发设计模式全解析
android
诺诺Okami2 小时前
Android Framework-WMS-层级结构树
android
alexhilton13 小时前
面向开发者的系统设计:像建筑师一样思考
android·kotlin·android jetpack
CYRUS_STUDIO1 天前
用 Frida 控制 Android 线程:kill 命令、挂起与恢复全解析
android·linux·逆向
CYRUS_STUDIO1 天前
Frida 实战:Android JNI 数组 (jobjectArray) 操作全流程解析
android·逆向
用户091 天前
Gradle Cache Entries 深度探索
android·java·kotlin
循环不息优化不止1 天前
安卓 View 绘制机制深度解析
android