Android Parcelable反序列化报错笔记:java.lang.RuntimeException: Unmarshalling unknown type code at offset(代码片

Posted xiong_it

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Parcelable反序列化报错笔记:java.lang.RuntimeException: Unmarshalling unknown type code at offset(代码片相关的知识,希望对你有一定的参考价值。

事件背景

有个实体类,利用Parcelable序列化保存在了本地,在新版本上新增了一个字段后,反序列化取数据时,报错如下:

java.lang.RuntimeException: Parcel android.os.Parcel: Unmarshalling
unknown type code xx at offset xx

代码示意

// 旧版本数据结构
public class ParcelableObject implements Parcelable {

	private int i;
	private float f;

    public ParcelableObject() {
    }


	// getter & setter

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.i);
        dest.writeFloat(this.f)
    }

    protected ParcelableObject(Parcel in) {
        this.i = in.readInt();
        this.f = in.readFloat();
    }

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

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

}

新版代码增加了一个newVar字段,同时按顺序加上了读写方法。

// 新的数据结构:同样代码做省略处理
public class ParcelableObject implements Parcelable {

	private int i;
	private float f;
	// 新增字段
	private int newVar;

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.i);
        dest.writeFloat(this.f);
        dest.writeInt(newVar);
        
    }

    protected ParcelableObject(Parcel in) {
        this.i = in.readInt();
        this.f = in.readFloat();
        try {
            // 防止旧版数据报错。
            this.newVar = in.readInt();
        } catch(Exception e) {
            // 报错时的兜底默认值
        	this.newVar = defautltValue;
        	// 报错不在这里,上面渠道newVar数据!!!!!!!!!
        }
    }

}

即使如上所示,添加了保护代码,可以依然报错:
java.lang.RuntimeException: Parcel android.os.Parcel: Unmarshalling unknown type code xx at offset xx

报错原理

序列化数据是一段连续的内存地址,会按照每个数据所占的大小去内存中取值,newVar取到下一段内存中的int值,可是每次取值都会根据该数据类型的所占位数产生一次偏移,所以导致整体数据去不全,最后报错。

解决办法

将newVar用对象类包装,取值失败,进入异常捕获,会进入默认的兜底默认值赋值,并且重设偏移量回到原偏移量

// 新的数据结构:同样代码做省略处理
public class ParcelableObject implements Parcelable {

	private int i;
	private float f;
	// 新增字段利用对象包装
	private NewVarObj newVarWrapper;

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.i);
        dest.writeFloat(this.f);
        dest.writeSerializable(newVarWrapper);
        
    }

    protected ParcelableObject(Parcel in) {
        this.i = in.readInt();
        this.f = in.readFloat();
        // 记录当前偏移量
        int positionOffset = in.dataPosition();
        try {
            // 防止旧版数据报错。因为是取对象值,无法取到,进入异常捕获流程,进行默认赋值
            this.newVar = (NewVarObj) in.readSerializable();
        } catch(Exception e) {
            // 报错时的兜底默认值
        	this.newVar = defautltValue;
        	// 将偏移量重设回去!!!!!!!!!
        	in.setDataPosition(positionOffset);
        }
    }

}

// 新字段包装类型
public class NewVarObj implements Serializable {

	private static final long serialVersionUID = -934758980857565111L;

	private int newVar;

	// getter & setter
}

以上是关于Android Parcelable反序列化报错笔记:java.lang.RuntimeException: Unmarshalling unknown type code at offset(代码片的主要内容,如果未能解决你的问题,请参考以下文章

Android Parcelable反序列化漏洞分析与利用

Android Parcelable反序列化漏洞分析与利用

Android Parcelable反序列化漏洞分析与利用

序列化与 Parcelable Android

序列化与反序列化之Parcelable和Serializable浅析

——Parcelable接口的使用(跨进程,Intent传输)