序列化:在数据传输中,如果要传输的是对象,这时候就用到我们的序列化和反序列化。有时候想将对象持久化储存在设备或者网络上,这时建议使用 Serializable
- Serializable : java 提供的接口 ,使用简单,直接实现这个接口就行。下面是一个使用栗子。
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 防止java.io.InvalidClassException
//private String testError;
private int userId;
private String userName;
public User(int userId, String userName) {
this.userId = userId;
this.userName = userName;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
值得注意的是: private static final long serialVersionUID = 1L;
建议我们使用 Serializable 接口的时候都手动去指定,虽然是非必须的,但是:如果不指定 serialVersionUID
,User 类序列化后,如果中途增加或减少一个成员变量(比如新增 private String testError;
),这时我们去反序列化将报错:java.io.InvalidClassException
为什么?序列化的时候系统把当前类的serialVersionUID
写入系列化文件中,当反序列化的时候会去检测文件中的serialVersionUID
,如果一致说明两者版本相同,否则说明两者相比发生了某些变化 ,这时就报错。
/**
* 序列化保存 Serializable 数据
*
* @param user
*/
private void saveAsSerializable(User user) {
Log.d("saveAsSerializable", "User:" + user.getUserName() + " " + user.getUserId());
FileOutputStream fos;
ObjectOutputStream oos;
try {
fos = getApplicationContext().openFileOutput("cache1.txt",
Context.MODE_PRIVATE);
oos = new ObjectOutputStream(fos);
oos.writeObject(user);
oos.close();
fos.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 反序列化读取 Serializable 数据
*
* @return
*/
private User readSerializable() {
User obj = null;
FileInputStream fis;
ObjectInputStream ois;
try {
fis = getApplicationContext().openFileInput("cache1.txt");
ois = new ObjectInputStream(fis);
obj = (User) ois.readObject();
ois.close();
fis.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (obj != null) {
Log.d("readSerializable", "User:" + obj.getUserName() + " " + obj.getUserId());
return obj;
} else {
return null;
}
}
- Parcelable:
Android 专用的一个接口,经别人测试,性能比 Serializable 好10倍左右,当在对象持久化储存时不建议使用这个。
下面是个栗子,其实编译器已经帮忙生成了大部分代码,我们只要写get
、set
和构造函数就行:
public class Human implements Parcelable {
private String address;
private boolean isMan;
private Woman woman;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public boolean isMan() {
return isMan;
}
public void setMan(boolean man) {
isMan = man;
}
public Woman getWoman() {
return woman;
}
public void setWoman(Woman woman) {
this.woman = woman;
}
public Human(String address, boolean isMan, Woman woman) {
this.address = address;
this.isMan = isMan;
this.woman = woman;
}
protected Human(Parcel in) {
address = in.readString();
isMan = in.readInt() == 1;
//反序列化 Woman 属性,由于 Woman 是另一个可序列化对象,所以它的反序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到该类的错误。
woman = in.readParcelable(Woman.class.getClassLoader());
}
public static final Creator CREATOR = new Creator() {
@Override
public Human createFromParcel(Parcel in) {
return new Human(in);
}
@Override
public Human[] newArray(int size) {
return new Human[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(address);
parcel.writeInt(isMan ? 1 : 0);
parcel.writeParcelable(woman, i);
}
}
这里敲一下黑板:
需要注意的是 Parcelable 不支持布尔值传输,怎样使用请看上面栗子;
当我们序列化的类中有其他对象,如例子上的 Woman 类 ,那么这个类也必须实现 Parcelable 接口,同时:反序列化 Woman 属性,由于 Woman 是另一个可序列化对象,所以它的反序列化过程需要传递当前线程的上下文类加载器,否则会报无法找到该类的错误(实现Parcelable 接口生成代码的时候已经自动写了)。(这里得说一下:生成代码前先把所有的成员变量都写了,方便自动生成,不然中途调加变量要相应的补上序列化和反序列化的实现)
下面是 Parcelable 的序列化与反序列化持久化存储的代码,建议 Intent 传输用 Parcelable 。
/**
* 序列化保存 Parcalable 数据
*
* @return
*/
private void saveParce(Human human) {
Log.d("loadParce", "Human:" + human.getAddress() + " " + human.getWoman().getName());
FileOutputStream fos;
try {
fos = getApplicationContext().openFileOutput("cache2.txt",
Context.MODE_PRIVATE);
BufferedOutputStream bos = new BufferedOutputStream(fos);
Parcel parcel = Parcel.obtain();
parcel.writeParcelable(human, 0);
bos.write(parcel.marshall());
bos.flush();
bos.close();
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 反序列化读取 Parcalable 数据
*
* @return
*/
private void loadParce() {
FileInputStream fis;
try {
fis = getApplicationContext().openFileInput("cache2.txt");
byte[] bytes = new byte[fis.available()];
fis.read(bytes);
Parcel parcel = Parcel.obtain();
parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0);
Human data = parcel.readParcelable(Human.class.getClassLoader());
Log.d("loadParce", "Human:" + data.getAddress() + " " + data.getWoman().getName()+"isMan: "+ data.isMan());
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
用 Intent 传输就贼简单了, putExtra 传过去后,采用Serializable 接口的用 getIntent().getSerializableExtra ()
、采用 Parcelable 接口的用getIntent().getParcelableExtra()
取出来就行。下面是网上看到的别人使用的代码(采用 Parcelable 接口):
MainActivity 中
Intent mTvOpenThird = new
Intent(MainActivity.this,ThirdActivity.class);
Pen tranPen = new Pen();
tranPen.setColor("big red");
tranPen.setSize(98);
// public Intent putExtra(String name, Parcelable value)
mTvOpenThird.putExtra("parcel_test",tranPen);
startActivity(mTvOpenThird);
ThirdActivity 中
Pen pen = (Pen)getIntent().getParcelableExtra("parcel_test");
mTvThirdDate = (TextView) findViewById(R.id.mTvThirdDate);
mTvThirdDate.setText("颜色:"+pen.getColor()+"\\n"
+"大小:"+pen.getSize());
- 小结一下:Serializable 使用简单但开销较大,序列化和反序列化过程需要大量I/O操作,建议都指定
serialVersionUID
,随便什么数值。所以如果考虑效率的话首选 Parcelable,Parcelable 主要用在内存序列化上,如果将对象持久化保存建议使用 Serializable 。Demo