在Android中,经常会遇到这样的情况,如何对两个Activity之间传递消息,熟悉Android开发的,肯定知道用Intent,对于自定义的Object,我们会使用Bundle
中的putSerializable()
,或者Bundle.putParcelable()
,可是我比较喜欢用Serialiable
的方式,因为简答啊,代码少啊,可是在Android中进程间通信里,我们要传递一个自定义的Object,需要使用Parceable
的方式,所以得搞搞它们。
Java中的序列化
在Java 1.1 增添了一种特性,叫“对象序列化”(Object Serialization)。它面向那些实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复原来的样子。这一过程可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。
对象序列化不仅保存了对象的“全景图”,而且能追踪对象内包含的所有句柄并保存那些对象,接着又能对每个对象内包含的句柄进行追踪,以此类推。
先看一个例子:
class Data implements Serializable{
private int i;
Data(int x){
i = x;
}
@Override
public String toString() {
return Integer.toString(i);
}
}
public class Worm implements Serializable {
private static int r(){
return (int)(Math.random()*10);
}
private Data[] d = {
new Data(r()),new Data(r()),new Data(r())
};
private Worm next;
private char c;
Worm(int i,char x){
System.out.println("Worm constructor: "+i);
c = x;
if(--i>0){
next = new Worm(i,(char)(x+1));
}
}
Worm(){
System.out.println("Default constructor");
}
@Override
public String toString() {
String s = ": "+c+"(";
for(int i = 0;i<d.length;i++) {
s += d[i].toString();
}
s+=")";
if(next!=null){
s+=next.toString();
}
return s;
}
public static void main(String[] args){
Worm worm = new Worm(6,'a');
System.out.println("worm = "+worm);
try {
ObjectOutputStream outputStream =
new ObjectOutputStream(new FileOutputStream("worm.out"));
outputStream.writeObject("Worm storage");
outputStream.writeObject(worm);
outputStream.close();
ObjectInputStream inputStream =
new ObjectInputStream(new FileInputStream("worm.out"));
String s = (String)inputStream.readObject();
Worm worm1 = (Worm)inputStream.readObject();
System.out.println(s+", w2 = "+worm1);
}catch (FileNotFoundException e){
}catch (IOException e){
}catch (ClassNotFoundException e){
}
}
}
输出结果:
Worm constructor: 6
Worm constructor: 5
Worm constructor: 4
Worm constructor: 3
Worm constructor: 2
Worm constructor: 1
worm = : a(158): b(633): c(934): d(547): e(919): f(612)
Worm storage, w2 = : a(158): b(633): c(934): d(547): e(919): f(612)
我们可以看到,在对一个Serialiable对象进行重新装配的过程中,不会调用任何构造器。整个对象都是从InputStream中取得数据恢复的。
序列化控制
如果我们希望对象的某一部分序列化,或者某一个子对象完全不必序列化,此时,可是通过实现Externliable
接口,用它替代Serialiable
接口,便可控制序列化的具体过程。Externliable
接口扩展了Serialiable
,并增添了两个方法:writeExternal()
和readExternal()
。在序列化装配的过程中,会自动调用这两个方法,以便我们执行一些特殊操作。
class Blip1 implements Externalizable {
public Blip1() {
System.out.println("Bilp1 Constructor");
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Bilp1 writeExternal");
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("Bilp1 readExternal");
}
}
class Bilp2 implements Externalizable{
Bilp2() {
System.out.println("Bilp2 Constructor");
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Bilp2 writeExternal");
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("Bilp2 readExternal");
}
}
public class Blips{
public static void main(String[] args){
Blip1 b1 = new Blip1();
Bilp2 b2 = new Bilp2();
try {
ObjectOutputStream outputStream =
new ObjectOutputStream(new FileOutputStream("Blips.out"));
System.out.println("Saving objects");
outputStream.writeObject(b1);
outputStream.writeObject(b2);
outputStream.close();
ObjectInputStream inputStream =
new ObjectInputStream(new FileInputStream("Blips.out"));
System.out.println("Recovering b1");
b1 = (Blip1)inputStream.readObject();
System.out.println("Recovering b2");
b2 = (Bilp2)inputStream.readObject();
}catch (FileNotFoundException e){
}catch (IOException e){
}catch (ClassNotFoundException e){
}
}
}
结果:
Bilp1 Constructor
Bilp2 Constructor
Saving objects
Bilp1 writeExternal
Bilp2 writeExternal
Recovering b1
Bilp1 Constructor
Bilp1 readExternal
Recovering b2
上面结果,我们可以看到b2没有被恢复,这是因为在上面代码中,b2的构造器不是public的。我们可以看到,在恢复b1的时候,会调用Blip1的构造器,这是和Serialiable
的不同。在Serialiable
中,对象完全以它保存下来的二进制位为基础恢复,不存在构造器调用。而对Externalizable
对象,所有普通的默认构建行为都会发生(包括在字段定义时的初始化)。
刚才我们有说,Externalizable
可是让我们自己控制,我们看看下面一个例子:
class Blip3 implements Externalizable{
public int x;
public int y;
public String s;
public Blip3(String s) {
super();
System.out.println("Bilp3(String s)");
this.s = s;
}
public Blip3() {
System.out.println("Bilp3 Default Constructor");
}
@Override
public String toString() {
System.out.println("toString");
return "toString: "+s+"---"+x+"--"+y;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Blip3 writeExternal");
out.writeObject(this.x);
out.writeObject(this.s);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
System.out.println("Blip3 readExternal");
this.x = (int)in.readObject();
this.s = (String) in.readObject();
}
}
public static void main(String[] args){
Blip3 b3 = new Blip3("333333333");
b3.x = 10;
b3.y = 11;
System.out.println(b3.toString());
try {
ObjectOutputStream outputStream =
new ObjectOutputStream(new FileOutputStream("Blip3.out"));
System.out.println("Saving objects");
//outputStream.writeObject(b1);
//outputStream.writeObject(b2);
outputStream.writeObject(b3);
outputStream.close();
ObjectInputStream inputStream =
new ObjectInputStream(new FileInputStream("Blip3.out"));
System.out.println("Recovering b3");
b3 = (Blip3) inputStream.readObject();
System.out.println(b3.toString());
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
输出结果:
Bilp3(String s)
toString
toString: 333333333---10--11
Saving objects
Blip3 writeExternal
Recovering b3
Bilp3 Default Constructor
Blip3 readExternal
toString
toString: 333333333---10--0
我们可以看到y是没有传过来的,而且Externalizable
会调用的是默认构造器,如果没有默认构造器,会报错。
在使用Serializable
序列化时,我们通常会指定一个serialVersionUID
,这个serialVersionUID
是来辅助序列化和反序列过程的,原则上序列化后的数据中的serialVersionUID
只有和当前类的serialVersionUID
相同,才能够被正常的反序列化。
serialVersionUID的工作机制:序列化的时候系统会把当前类的serialVersionUID写入系列化文件中(也可能是其他中介),当序列化的时候系统回去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一直就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化。否则就说明当前类和序列化的类相比发生了某些变化,比如成员变量的数量,类型可能发何时能了改变,这个时候是无法正常反序列化的。
Parcelable的使用
Parceable也是一个接口,只要实现这个接口,一个类的对象就可以实现序列化并通过Intent和Binder传递。
public class User implements Parcelable {
public int userId;
public String userName;
public boolean isMale;
public User(int userId, String userName, boolean isMale) {
this.userId = userId;
this.userName = userName;
this.isMale = isMale;
}
private User(Parcel in){
this.userId = in.readInt();
this.userName = in.readString();
this.isMale = in.readInt() == 1;
}
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];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(userId);
dest.writeString(userName);
dest.writeInt(isMale?1:0);
}
public static void putUser(Context context,Class activity){
Intent intent = new Intent(context,activity);
Bundle bundle = new Bundle();
bundle.putParcelable("user",new User(1,"vivian",false));
intent.putExtra("user",bundle);
}
}
Parcel内部包装了可序列化的数据,在意在Binder中自由传出。序列化使由writeToParcel
方法来完成的,最终是通过Parcel中的一系列wirte方法来完成的。反序列功能右CREATOR来完成,其内部标明了如何创建序列化对象和数组,并通过一系列read方法来完成反序列化的过程。
内容描述功能又describeContents方法来完成,几乎所有情况下这个方法都应该返回0,仅当当前对象中存在文件描述符时,此方法返回1.
Serializable和Parcable的区别
可是肯定的是,两者都支持序列化和反序列的操作。
两者最大的区别在于存储媒介不同,Serializable
使用I/O读写存储在硬盘上,而Parcable
是直接在内存中读写。很明显,内存的读写速度通常大于IO读写,所以在Android中传递数据优先选择Parcelable
。
Serializable
会使用反射,序列化和反序列过程需要大量I/O操作,Parcelable
自己实现封装和解封操作不需要用反射,数据也存放在Natvie内存中,效率要快很多,
在两个Activity之间传递对象需要注意:
对象的大小!!!
Intent 中的 Bundle 是使用 Binder 机制进行数据传送的。能使用的 Binder 的缓冲区是有大小限制的(有些手机是 2 M),而一个进程默认有 16 个 Binder 线程,所以一个线程能占用的缓冲区就更小了( 有人以前做过测试,大约一个线程可以占用 128 KB)。所以当你看到 The Binder transaction failed because it was too large 这类 TransactionTooLargeException 异常时,你应该知道怎么解决了。