问题来源
项目开发过程中,实体类实现Parcelable接口是常见的事情,实体类除了要实现它的几个方法之外,还另外要定义一个静态常量CREATOR,如下例所示:
public class DataEntity<T extends Parcelable> implements Parcelable {
private String str;
private int position;
private T data;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.str);
dest.writeInt(this.position);
dest.writeParcelable(this.data, flags);
}
public DataEntity() {
}
protected DataEntity(Parcel in) {
this.str = in.readString();
this.position = in.readInt();
this.data = in.readParcelable(T.class.getClassLoader());
}
public static final Creator<DataEntity> CREATOR = new Creator<DataEntity>() {
@Override
public DataEntity createFromParcel(Parcel source) {
return new DataEntity(source);
}
@Override
public DataEntity[] newArray(int size) {
return new DataEntity[size];
}
};
}
看完上面的代码,您觉得他会正常运行吗?答案肯定是不能,不然我也没必要写这篇文章了。
仔细观察上面代码就会发现这个实体类中有个泛型存在,泛型在反序列化的时候,没有具体类型,拿不到它的CREATOR!
所以如果拿不到CREATOR,那么就无法执行反序列化,同时Android系统的源码我们不能去修改,只能寻找其他的途径了。
错误的解决方案
网络流传了一种解决方案是:把泛型数据用Bundle包装,然后序列化和反序列化这个Bundle对象,进过测试发现这种方案行不通。代码如下:
//write
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(DATA_KEY, data);
dest.writeBundle(bundle);
//read
this.data = in.readBundle().getParcelableArrayList(DATA_KEY);
最终解决方案
经过以上的分先发现,反序列化之所失败,就是因为我们拿不到泛型对应类的classLoader,那么从这个角度出发,我们是否可以序列化的时候先保存这个泛型的标准类名(报名+类名,例如:com.feeyo.DataEntity),然后再反序列化的时候利用Java 反射得到classLoader。有了思路,代码如下:
public class DataEntity<T extends Parcelable> implements Parcelable {
private String str;
private int position;
private T data;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.str);
dest.writeInt(this.position);
dest.writeString(data.getClass().getName());
dest.writeParcelable(this.data, flags);
}
public DataEntity() {
}
protected DataEntity(Parcel in) {
this.str = in.readString();
this.position = in.readInt();
String dataName = in.readString();
try {
this.data = in.readParcelable(Class.forName(dataName).getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static final Creator<DataEntity> CREATOR = new Creator<DataEntity>() {
@Override
public DataEntity createFromParcel(Parcel source) {
return new DataEntity(source);
}
@Override
public DataEntity[] newArray(int size) {
return new DataEntity[size];
}
};
}