import android.os.Parcel;
import android.os.Parcelable;
public class Test implements Parcelable {
private int a;
private String s;
protected Test(Parcel in) {
a = in.readInt();
s = in.readString();
}
public static final Creator CREATOR = new Creator() {
@Override
public Test createFromParcel(Parcel in) {
return new Test(in);
}
@Override
public Test[] newArray(int size) {
return new Test[size];
}
};
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
dest.writeString(s);
}
}
这里稍作解释:
1、describeContents(),该方法不用管,一般直接reurn 0就可以了。
2、writeToParcel,该部分是序列化时候写的逻辑,调用的事Parcel提供的API,可以writeInt、writeLong、writeString等,基本数据类型都可以支持。
3、Creator接口实现类,这个一般也都是模板化的,只需要泛型替换成当前需要序列化的类即可。
4、形参为Parcel的构造方法,构造方法是序列化读的逻辑,和写过程对应,也都是利用Parcel提供的API进行读取,基本数据类型当然可以支持的,调用readInt、readLong、readString即可。
上面需要注意的一点是成员变量写的顺序要和读的顺序保持一致,因为序列化其实就是把类的成员信息写到二进制流里面,接收端接受二进制流然后解析二进制流,构造我们需要的对象。
这里的意思是我们要序列化的Bean A里面有一个成员变量的数据类型是另一个Bean B,这时候有两种处理方法:
(1)B类实现Serializable接口
实现Serializable接口很简单,只需要加上implements Serializable即可,也不需要实现什么方法。那么在A类里面在writeToParcel方法里面,往Parcel里面写入的时候,调用方法
public final void writeSerializable(Serializable s)。在构造方法里面从Parcel里面读取的时候,调用public final Serializable readSerializable(),如下面代码所示:
import android.os.Parcel;
import android.os.Parcelable;
import java.util.List;
public class Test implements Parcelable {
private int a;
private String s;
private Another another;
。。。。。
protected Test(Parcel in) {
a = in.readInt();
s = in.readString();
another = (Another) in.readSerializable();
}
。。。。。。
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
dest.writeString(s);
dest.writeSerializable(another);
}
}
(2)B类实现Parcelable接口
B类实现Parcelable接口,A类的writeToParcel方法里面,往Parcel里面写入的时候,调用方法
public final void writeParcelable(Parcelable p, int parcelableFlags),在构造方法里从Parcel读取的时候,调用方法public final
public class Test implements Parcelable {
private int a;
private String s;
private Another another;
。。。。。。
protected Test(Parcel in) {
a = in.readInt();
s = in.readString();
another = in.readParcelable(Another.class.getClassLoader());
}
。。。。。。
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
dest.writeString(s);
dest.writeParcelable(another,0);
}
}
这里又要根据List里面的元素的实际类型来区分处理:
(1)List的元素类型是String类型,我们在writeToParcel方法里面,往Parcel里面写入的时候,调用方法
public final void writeStringList(List
import android.os.Parcel;
import android.os.Parcelable;
import java.util.List;
public class Test implements Parcelable {
private int a;
private String s;
private List strList;
。。。。。
protected Test(Parcel in) {
a = in.readInt();
s = in.readString();
strList = in.createStringArrayList();
}
。。。。。
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
dest.writeString(s);
dest.writeStringList(strList);
}
}
(2)List的元素类型是自定义对象类型,那么这个自定义对象必须实现Parcelable接口,Serializable接口这种情境下是不支持的。这种情况下,我们在writeToParcel方法里面,往Parcel里面写入的时候,调用方法
public final
public class Test implements Parcelable {
private int a;
private String s;
private List anothers;
。。。。。。
protected Test(Parcel in) {
a = in.readInt();
s = in.readString();
anothers = in.createTypedArrayList(Another.CREATOR);
}
。。。。。。
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
dest.writeString(s);
dest.writeTypedList(anothers);
}
}
这里补充提示一点,List里面的元素如果没有实现Parcelable接口,是不能被Parcelable支持的。
如果遇见数组类型的成员变量,比如int[],我们在writeToParcel方法里面
往Parcel里面写入的时候,可以调用其方法
public final void writeIntArray(int[] val) {
if (val != null) {
int N = val.length;
writeInt(N);
for (int i=0; i
在构造方法里面从Parcel里面读取的时候可以调用其方法
public final void readIntArray(int[] val) {
int N = readInt();
if (N == val.length) {
for (int i=0; i
这里之所以要贴一下源码,是因为从源码我们要知道一个问题,就是我们的数组对象必须是一个长度确定的数组,否则就会抛异常。
遇见成员类型是Map时,其实key和value是基本数据类型、或者是实现了Parcelable或者Serializable接口的自定义类时,都是可以被支持的。在writeToParcel方法里面
往Parcel里面写入的时候,可以调用其方法
public final void writeMap(Map val) {
writeMapInternal((Map) val);
}
在构造方法里面从Parcel里面读取的时候可以调用其方法
public final void readMap(Map outVal, ClassLoader loader) {
int N = readInt();
readMapInternal(outVal, N, loader);
}
示例代码如下:
public class Test implements Parcelable {
private int a;
private String s;
private Map map;
。。。。。。
protected Test(Parcel in) {
a = in.readInt();
s = in.readString();
map = new HashMap<>();
in.readMap(map,getClass().getClassLoader());
}
。。。。。。
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(a);
dest.writeString(s);
dest.writeMap(map);
}
}
需要注意一点的是,在读取的时候,调用readMap方法是需要传入一个类构造器的,这个一定要注意,如果你传的是String.class.getClassLoader或者Integer.class.getClassLoader,那么如果key或者value是自定义类型,那么可能会导致类构造器找不到类,因为String类的构造器是RootClassLoader,其找不到我们应用程序自定义的类,会直接报异常如下:
Caused by: java.lang.ClassNotFoundException: com.example.localuser.retrofittest.SerializeTest.Another
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:453)
at android.os.Parcel$2.resolveClass(Parcel.java:2927)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1615)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1520)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1776)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
解决办法就是直接传入当前序列化的类的类构造器就可以了,该类构造器也可以满足String、Integer等对象的构造,因为系统构造器找不到类会交给父类构造器去查找类的,这是类构造器的双亲委派机制,所以这个地方的类构造器选择级别低的构造器就不会有问题了。