我当然不会无聊地介绍 Serializable 与 Parcelable 的使用差别,本文重点是解释 Parcelable 为什么要设计成这样。
Serializable
是 Java 提供的一个序列化接口,Parcelable
是 Android 提供的一个序列化接口。为什么要提供两个呢?为什么 Parcelable 要写那么多东西呢?
用法
先简单说明一下两个的写法,以便更好解释。
/** 需要序列化的一个类 */
class Entity {
private int param1;
private String param2;
}
Serializable 实现序列化
class Entity implements Serializable {
private int param1;
private String param2;
}
Serializable
只需要让需要序列化的类和其属性继承该接口即可。
Serializable 接口的作用其实是 声明,声明该类可以被正确地序列化。而真正的序列化是交给 Java 实现。
它的具体用法不是本文重点,故略。
Parcelable 实现序列化
class Entity implements Parcelable {
private int param1;
private String param2;
protected Entity(Parcel in) {
param1 = in.readInt();
param2 = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(param1);
dest.writeString(param2);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Entity> CREATOR = new Creator<Entity>() {
@Override
public Entity createFromParcel(Parcel in) {
return new Entity(in);
}
@Override
public Entity[] newArray(int size) {
return new Entity[size];
}
};
}
Parcelable
实现序列化居然要额外产生这么多东西!那么为什么它要设计得这么麻烦呢,是为了解决什么问题?
原理
Serializable 的安全问题
被 Serializable 声明的类,它的反序列化是交给 ObjectInputStream.readObject
实现的。这个方法可以将类路径上几乎任何类型的对象都实例化。
而因为该方法可以执行任意类型的反序列化代码,就留下了被利用的可能。并且,反序列化的数据是外部提供的,被不信任的数据直接产生的对象是有风险的。
解决思路如下:
1、使用 Java 9 时发明的 ObjectInputFilter
,它可以指定接受或者拒绝某些类。
2、不使用 Java 的 Serializable,使用 JSON、protobuf 等结构化数据表示方案。
3、使用序列化代理代替序列化实例。
Parcelable 的序列化代理
序列化代理的详细阐述可以参考《Effective Java》第 90 条:考虑用序列化代理代替序列化实例。
从思路上说,不安全是因为用不可控的数据直接构造了实例。但如果能进行一层数据筛选,把需要的参数通过类的构造方法构造,那么它就是安全可控的了。
对应到 Parcelable
的这一堆代码就很好理解了。Entity 类不直接反序列化,而是通过 Parcel 进行构造,那么上述的安全问题就可以避免了。
Parcelable 的其他部分不是本文重点,故略。