Serializable方式
Serializable是Java 提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操作,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。至于序列化的方法也很简单,只需要让一个类去实现Serializable这个接口就可以了。
比如说有一个Person类,其中包含了name和age这两个字段,想要将它序列化就可以这样写:
public class Person implements Serializable {
privateString name;
privateint age;
publicString getName() {
return name;
}
publicvoid setName(String name) {
this.name = name;
}
publicint getAge() {
return age;
}
publicvoid setAge(int age) {
this.age = age;
}
}
这里让Person类去实现了Serializable接口,这样所有的Person对象就都是可序列化的了。接下来在FirstActivity中的写法非常简单:
Person person =newPerson();
person.setName("Tom");
person.setAge(20);
Intent intent =newIntent(FirstActivity.this,SecondActivity.class);
intent.putExtra("person_data", person);
startActivity(intent);
可以看到,这里我们创建了一个Person的实例,然后就直接将它传入到putExtra()方法中了。由于Person类实现了Serializable接口,所以才可以这样写。
接下来在SecondActivity中获取这个对象也很简单,写法如下:
Person person =(Person) getIntent().getSerializableExtra("person_data");
这里调用了getSerializableExtra()方法来获取通过参数传递过来的序列化对象,接着再将它向下转型成Person对象,这样我们就成功实现了使用Intent来传递对象的功能了。
如果你想要将对象序列化存储到本地文件中,除了要实现 Serializable 接口外,还要在类的声明中指定下面的标识,采用ObjectOutPutStream 和 ObjectInputStream即可实现
private static final long serialVersionUID = 8711368828010083044L;
public class User implements Serializable {
private static final long serialVersionUID=1L;
public int userId;
public String userName;
public boolean isMale;
......
}
恢复后的newUser对象虽然和user的内容完全一样,但是两者并不是同一个对象
//序列化过程
User user = new User();
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cache.text"));
outputStream.writeObject(user);
outputStream.close();
//反序列过程
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cache.text"));
User newUser = (User) inputStream.readObject();
//序列化过程
User user = new User();
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cache.text"));
outputStream.writeObject(user);
outputStream.close();
//反序列过程
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cache.text"));
User newUser = (User) inputStream.readObject();
这个serialVersionUID 是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID 只有和当前类的serialVersionUID 相同才能够正常的被反序列化。它的工作机制是这样的,序列化的时候系统会把当前类的serialVersionUID 写入序serialVersionUID列化的文件中(也可能是其他中介),当反序列化的时候,系统会去检测文件中的serialVersionUID ,看它是否和当前类的 serialVersionUID 一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功的反序列化,否则就说明当前类和序列话的类相比发生了某些变化,比如成员变量的数量。类型可能发生了改变,这个时候是无法正常反序列话化的,会报如下错误:
java.io.InvalidClassException:Main; local class incompatible: stream classdesc serialVersionUID = 8711368828010083044,
local class serialVersionUID = 8711368828010083043
手动给serialVersionUID指定一个值,如 1L,可极大的避免反序列化过程的失败,如不指定serialVersionUID的值,反序列话时,当前类有所改变,比如版本升级后我们可能增加或删除了某个成员变量,那么系统就会重新计算当前类的 hash值,并将它赋值给serialVersionUID,这个当前类的serialVersionUID就和序列化数据中的serialVersionUID不一致,序列化失败。
如果类发生了结构性的变化,比如修改了类名,修改了成员变量的类型,这个时候尽管serialVersionUID验证通过了,但是反序列化过程还是会失败,因为类结构发生了毁灭性的改变,根本无法从老版本的数据中还原出一个新的类结构对象
Parcelable方式
除了Serializable之外,使用Parcelable也可以实现相同的效果,不过不同于将对象进行序列化,Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就使用Intent和Binder实现传递对象的功能了。下面我们来看一下Parcelable的实现方式,修改Person中的代码,如下所示:
public class Person implements Parcelable
{
privateString name;
privateint age;
……
@Override
publicint describeContents () {
return 0;
}
@Override
publicvoid writeToParcel (Parcel dest,int flags){
dest.writeString(name);// 写出name
dest.writeInt(age);// 写出age
}
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator < Person > () {
@Override
publicPerson createFromParcel (Parcel source){
Person person = newPerson();
person.name = source.readString();// 读取name
person.age = source.readInt();// 读取age
return person;
}
@Override
publicPerson[] newArray ( int size){
return new Person[size];
}
} ;
}
首先我们让Person类去实现了Parcelable接口,这样就必须重写describeContents()和writeToParcel()这两个方法。其中describeContents()方法几乎在所有情况下都应该返回0,仅仅当当前对象中存在文件描述时,此方法返回1.(返回1这里不太明白,有大神解读下吗?)而writeToParcel()方法中我们需要调用Parcel的writeXxx()方法将Person类中的字段一一写出。注意字符串型数据就调用writeString()方法,整型数据就调用writeInt()方法,以此类推。
除此之外,我们还必须在Person类中提供一个名为CREATOR的常量,这里创建了Parcelable.Creator接口的一个实现,并将泛型指定为Person。接着需要重写createFromParcel()和newArray()这两个方法,在createFromParcel()方法中我们要去读取刚才写出的name和age字段,并创建一个Person对象进行返回,其中name和age都是调用Parcel的readXxx()方法读取到的,注意这里读取的顺序一定要和刚才写出的顺序完全相同。而newArray()方法中的实现就简单多了,只需要new出一个Person数组,并使用方法中传入的size作为数组大小就可以了。
接下来在FirstActivity中我们仍然可以使用相同的代码来传递Person对象,只不过在SecondActivity中获取对象的时候需要稍加改动,如下所示:
Person person =(Person) getIntent().getParcelableExtra("person_data");
注意这里不再是调用getSerializableExtra()方法,而是调用getParcelableExtra()方法来获取传递过来的对象了,其他的地方都完全相同。
public class User implements Parcelable {
public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
public int userId;
public String userName;
public boolean isMale;
public Book book;
protected User(Parcel in) {
userId = in.readInt();
userName = in.readString();
isMale = in.readByte() == 1;
book=in.readParcelable(Thread.currentThread().getContextClassLoader());
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(userId);
parcel.writeString(userName);
parcel.writeByte((byte) (isMale ? 1 : 0));
parcel.writeParcelable(book,0);
}
}
这里要注意,在User(Parcel in)方法中,由于book是另一个可序列化对象,所以它的序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到类的错误。
Serializable 和 Parcelable 区别
Android 中自定义的对象序列化的问题有两个选择一个是Parcelable,另外一个是Serializable(Java)。
一 序列化原因:
1、 永久性保存对象,保存对象的字节序列到本地文件中;
2、通过序列化对象在网络中传递对象;
3、通过序列化在进程间传递对象。
二 至于选取哪种可参考下面的原则:
1、 在使用内存的时候,Parcelable 类比Serializable性能高,所以推荐使用Parcelable类。
2、Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
3、如要要将数据储存在磁盘上,使用Serializable,因为在外界有变化的情况下,Parcelable不能很好的保证数据的持续性,Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。
实现方法:
①Serializable 的实现,只需要继承 implements Serializable 即可。这只是给对象打了一个标记,系统会自动将其序列化。
②Parcelabel 的实现,需要在类中添加一个静态成员变量 CREATOR,这个变量需要继承 Parcelable.Creator 接口。