概述
在Android中,IPC需要先将对象进行序列化,在完成对象序列化之后,通过Intent和Binder等传输数据。序列化方式主要有Serializable和Parcelable。 其中Serializable是Java提供的一个对象序列化接口,Parcelable是Android提供的对象序列化接口。
Serializable
Serializable的使用很简单,只需要实现接口,就能完成对象的序列化和反序列化。例如,有一个类Person,只要User实现Serializable接口就能实现 对象的序列化和反序列化。
import java.io.Serializable;
public class User implements Serializable{
private static final long seriaVersionUID = 1L;
private String name;
private int age;
private String male;
public User(String name, int age, String male) {
super();
this.name = name;
this.age = age;
this.male = male;
}
}
我们通过ObjectOutputStream和ObjectInputSttream实现对象的序列化过程:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class TextMain {
public static void main(String[] args) throws ClassNotFoundException {
// 序列化
User user = new User("david",22,"male");
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.txt"));
out.writeObject(user);
out.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 反序列化
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.txt"));
User newUser = (User)in.readObject();
System.out.println("姓名:" + newUser.getName());
System.out.println("年龄:" + newUser.getAge());
System.out.println("性别:" + newUser.getMale());
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
查看输出:
反序列化成功,说明序列化也成功了。但是这里要注意的是,序列化前后两个对象并不是同一个对象,仅仅是对象的值相同。
我们注意到User对象中有一个属性:private static final long seriaVersionUID = 10086L;
我们手动指定了这个属性的值为10086L,其实没有这个值,在上面的代码中也会成功。这个类的作用主要是:
在序列化时,系统会将seriaVersionUID写入到序列化文件中,反序列化时,会检测当前类的seriaVersionUID和序列化文件中的seriaVersionUID 是否一致,如果相同,说明序列化和反序列化成功,否则说明当前的类和序列化的类不同,序列化失败。
seriaVersionUID的指定有两种方式,一种是手动指定,一种是让编译器根据当前类的结构生成hash值。
如果要尽量保持序列化的成功,应该使用手动。如果使用编译器生成的seriaVersionUID,只要类发生了变化,hash值就会变灰,序列化就会一定失败。 而使用手动指定,在序列化前后,seriaVersionUID是一定的,即使因为版本升级等原因,类的属性增加或者减少,都会尽最大努力保持序列化成功。但是 即使手动指定了seriaVersionUID,也不能保证序列化和反序列化一定就成功,如果发生了类的结构性变化,比如改变类名,还是会失败的。
另外:
(1)静态成员变量属于类而不属于对象,不会参加序列化和反序列化;
(2)被transient修饰的成员变量不参与序列化过程。
需要注意transient关键字,被它修饰的变量不会参与serializable序列化,但是会被Externalizable序列化。
Parcelable
Parcelable是Android专用的序列化和反序列化方法。和serializable一样,需要实现接口,对象就能够通过Intent和Binder传递。
首先定义两个类:
package com.example.daihao.servicetest;
import android.os.Parcel;
import android.os.Parcelable;
public class Car implements Parcelable {
private String holder;
private int price;
public Car(String holder, int price) {
this.holder = holder;
this.price = price;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(holder);
dest.writeInt(price);
}
public static final Parcelable.Creator<Car> CREATOR = new Parcelable.Creator<Car>(){
@Override
public Car createFromParcel(Parcel source) {
return null;
}
@Override
public Car[] newArray(int size) {
return new Car[size];
}
};
private Car(Parcel in){
holder = in.readString();
price = in.readInt();
}
}
Car作为User的属性,也需要完成序列化。
package com.example.daihao.servicetest;
import android.os.Parcel;
import android.os.Parcelable;
public class User implements Parcelable {
private String userName;
private int age;
private String male;
private Car car;
/**
* 序列化
*
* @param dest
* @param flags 绝大多数情况下是0.为1时,当前对象作为返回值使用,不能立即释放
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(userName);
dest.writeInt(age);
dest.writeString(male);
dest.writeParcelable(car, 0);
}
/**
* 反序列化
*/
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>(){
@Override
public User createFromParcel(Parcel source) {
return new User(source);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
private User(Parcel in){
userName = in.readString();
age = in.readInt();
male = in.readString();
car = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
/**
* 当前对象的内容描述。
* 绝大多数情况下返回0;
* 返回1表示该对象含有文件描述符
* @return
*/
@Override
public int describeContents() {
return 0;
}
public User(String userName, int age, String male, Car car) {
this.userName = userName;
this.age = age;
this.male = male;
this.car = car;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getMale() {
return male;
}
public void setMale(String male) {
this.male = male;
}
}
使用Parcelable进行序列化,需要复写如下方法:
(1)public void writeToParcel(Parcel dest, int flags):序列化方法,参数flags,绝大多数情况下是0。为1时,当前对象作为返回值使用,不能立即释放;
(2)public static final Parcelable.Creator
(3)public int describeContents():当前对象的内容描述。绝大多数情况下返回0;返回1表示该对象含有文件描述符
区别
在Android记住使用Parcelable就OK了。区别在性能上:Parcelable更好。Serializable是Java提供的接口,需要大量IO操作,开销较大。