Intent(意图)和序列化和反序列化

 

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中的写法非常简单:


 
  1. Person person =newPerson();
  2. person.setName("Tom");
  3. person.setAge(20);
  4. Intent intent =newIntent(FirstActivity.this,SecondActivity.class);
  5. intent.putExtra("person_data", person);
  6. startActivity(intent);

可以看到,这里我们创建了一个Person的实例,然后就直接将它传入到putExtra()方法中了。由于Person类实现了Serializable接口,所以才可以这样写。

接下来在SecondActivity中获取这个对象也很简单,写法如下:


 
  1. 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中获取对象的时候需要稍加改动,如下所示:


 
  1. 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 接口。

 

 

 

 

发布了38 篇原创文章 · 获赞 5 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章