首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Parcelable使用时java.lang.RuntimeException: Parcel android.os.Parcel@xxxx: Unmarshalling unknown typ...

Parcelable使用时java.lang.RuntimeException: Parcel android.os.Parcel@xxxx: Unmarshalling unknown typ...

作者头像
木易士心
发布2025-11-30 10:02:35
发布2025-11-30 10:02:35
140
举报

背景

记一次 Parcelable 序列化不一致导致的运行时错误

错误截图
错误截图

在 Android 开发过程中,我们经常会使用 Parcelable 接口来实现对象在组件(如 Activity 或 Fragment)之间的高效传递。虽然 Parcelable 性能优异,但其手动序列化和反序列化的机制也容易因疏忽引发问题。

最近在开发中就遇到了一个看似简单却耗费了不少排查时间的问题——在通过 Intent 传递一个自定义 Parcelable 对象时,应用在反序列化阶段崩溃,报错信息指向 Parcel 读取异常。

问题场景

问题出现在一个名为 MediaItem 的可序列化类中。由于业务需求变化,我为该类新增了两个字段:widthheight(宽度和高度),并在 writeToParcel() 方法中正确添加了写入逻辑:

代码语言:javascript
复制
@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeLong(id);
    dest.writeString(mimeType);
    dest.writeParcelable(uri, 0);
    dest.writeParcelable(thumbUri, 1);
    dest.writeLong(size);
    dest.writeLong(duration);
    dest.writeInt(height);
    dest.writeInt(width);
}

然而,在反序列化构造函数中,我遗漏了对这两个新字段的读取

代码语言:javascript
复制
private MediaItem(Parcel source) {
    id = source.readLong();
    mimeType = source.readString();
    uri = source.readParcelable(Uri.class.getClassLoader());
    thumbUri = source.readParcelable(Uri.class.getClassLoader());
    size = source.readLong();
    duration = source.readLong();
    height = source.readInt();
    width = source.readInt();
}

注意:虽然上面的代码看起来“读写顺序一致”,但如果你在添加字段时只写了 writeInt(height)writeInt(width),却忘了在构造函数中调用 readInt() 来读取它们,就会导致后续字段的读取错位,从而引发 BadParcelableException 或类型不匹配错误。

根本原因

Parcelable 的序列化机制依赖于写入和读取顺序的严格一致。当你向 Parcel 中写入数据时,系统会按顺序存储;而在反序列化时,也必须按照完全相同的顺序逐个读取。

一旦写入和读取的字段数量或顺序不匹配,就会导致:

  • 后续字段读取错位(例如把 int 当作 long 读取)
  • 抛出 Parcel android.os.Parcel@4265a958: Unmarshalling unknown type code 等诡异异常
  • 应用崩溃且堆栈信息不易定位问题根源

解决方案

确保 writeToParcel()Parcelable 构造函数中的读写操作一一对应、顺序一致

修改后的完整代码如下:

代码语言:javascript
复制
public class MediaItem implements Parcelable {
    private long id;
    private String mimeType;
    private Uri uri;
    private Uri thumbUri;
    private long size;
    private long duration;
    private int height;
    private int width;

    // Parcelable 构造函数:必须按写入顺序读取
    private MediaItem(Parcel source) {
        id = source.readLong();
        mimeType = source.readString();
        uri = source.readParcelable(Uri.class.getClassLoader());
        thumbUri = source.readParcelable(Uri.class.getClassLoader());
        size = source.readLong();
        duration = source.readLong();
        height = source.readInt();  // 新增:读取 height
        width = source.readInt();   // 新增:读取 width
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(id);
        dest.writeString(mimeType);
        dest.writeParcelable(uri, 0);
        dest.writeParcelable(thumbUri, flags);
        dest.writeLong(size);
        dest.writeLong(duration);
        dest.writeInt(height);      // 新增:写入 height
        dest.writeInt(width);       // 新增:写入 width
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<MediaItem> CREATOR = new Creator<MediaItem>() {
        @Override
        public MediaItem createFromParcel(Parcel source) {
            return new MediaItem(source);
        }

        @Override
        public MediaItem[] newArray(int size) {
            return new MediaItem[size];
        }
    };
}

预防建议

  1. 写完 writeToParcel 后立即检查反序列化构造函数,确保每一步读取都对应一次写入。
  2. 使用 Android Studio 插件(如 ParcelableGenerator)来自动生成 Parcelable 代码,减少人为错误。
  3. 在修改已有 Parcelable 类时,务必重新测试序列化流程,尤其是在跨进程或跨 Activity 传递时。

总结

这个错误虽小,却极具迷惑性。它不会在编译期报错,而是在运行时才暴露,且错误信息往往不够直观。关键在于理解 Parcelable 的底层机制:它不是基于字段名,而是基于写入/读取的顺序

只要记住:“写一个,读一个;顺序不能乱”,就能有效避免此类问题。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 问题场景
  • 根本原因
  • 解决方案
  • 预防建议
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档