Android序列化:Serializable和Parcelable详解

前言

在Android里面,我们常常会遇到下面情形:
1.使用Intent和Binder进行数据传输
2.将对象持久化到存储设备上
3.将对象通过网络传输到给其他客户端
此时,我们就需要用到Serializable或Parcelable。

Serializable

Serializable是java的一个空接口,主要是为对象提供了标准的序列化和反序列化操作。使用起来是非常简单的,你只需要添加如下的简单表示就可自动实现默认的序列化过程。

private static final long serialVersionUID = -6053329124108300886L;

SerialVersionUID的作用是什么呢?我们实现了Serializable接口,不声明SerialVersionUID会有怎样 的后果呢? 如果我们不声明这个标识,我们的序列化过程是不受任何影响的,但是对反序列化过程会有影响的。下面我们用实际代码来说明:

public class Book implements Serializable{

    private static final long serialVersionUID = 1L;

    private int bookId;
    private String bookName;

    public Book(int bookId,String bookName){
        this.bookId = bookId;
        this.bookName = bookName;
    }
    public int getBookId() {
        return bookId;
    }
    public void setBookId(int bookId) {
        this.bookId = bookId;
    }
    public String getBookName() {
        return bookName;
    }
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
}
        // 序列化过程
        Book book = new Book(110, "百年孤独");
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("book.txt"));
        out.writeObject(book);
        out.close();

        // 反序列化过程
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("book.txt"));
        Book newBook = (Book) in.readObject();
        in.close();

需要注意的是,book和newBook并不是同一个对象。一开始我们提到,不需要指定serialVersionUID也可以实现序列化,但是反序列化会受影响。为什么呢?原因是,这个serialVersionUID起到的是一个标识作用,主要是辅助序列化和反序列化过程。序列化的时候,会将serialVersionUID存入序列化的文件,而反序列化的时候,系统会去检 测文件中的serialVersionUID,判断是否与当前类的serialVersionUID一致。一致则成功反序列化;如果不一致,if出现类的成员变量的数量发生了变化,或是变量类型改变了等情况,就会反序列化失败,也会报出以下错误:

java.io.InvalidClassException:Main;local class incompatible:stream classdesc serialVersionUID = -6053329124108300886L,local class serialVersionUID = -6053329124108311568L。

也就是serialVersionUID的值我们可以手动去设置,一般情况设置为1L,或者让eclipse为我们生成hash值, 其实两者并没有实质性的区别。效果完全一样。需要注意的是,静态变量属于类,不属于对象,不会参与序列化过程。使用transient关键字修饰的成员变量也不会参与序列化过程。


Parcelable

在Android中也提供了新的序列化方式,那就是Parcelable接口。实现了Parcelable接口,那么这个类的对象 就实现了序列化,并且可以通过Intent和Binder传递。下面我们写一下基本的用法:

public class Book implements Parcelable{

    public int bookId;
    public String bookName;
    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    /***
     * 序列化
     */
    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(bookId);
        out.writeString(bookName);
        out.recycle();
    }

    /***
     * 反序列化
     */
    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {

        @Override
        public Book createFromParcel(Parcel in) {

            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            // TODO Auto-generated method stub
            return new Book[size];
        }
    };

    private Book(Parcel in){
        this.bookId = in.readInt();
        this.bookName = in.readString();
    }
}
public class User implements Parcelable {
    public int userId;
    public String userName;
    public Book book;

    @Override
    public int describeContents() {
        // TODO Auto-generated method stub
        return 0;
    }

    /****
     * 序列化
     */
    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(userId);
        out.writeString(userName);
        out.writeParcelable(book, flags);
        out.recycle();
    }
    /***
     * 反序列化
     */
    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {

        @Override
        public User createFromParcel(Parcel in) {

            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            // TODO Auto-generated method stub
            return new User[size];
        }
    };

    private User(Parcel in){
        this.userId = in.readInt();
        this.userName = in.readString();
        this.book = in.readParcelable(Thread.currentThread().getContextClassLoader());
    }
}

需要注意的是User类里的User(Parcel in)里,由于book也是一个可序列化对象,那么它的反序列化过程则需 要传递当前线程的上下文对象,否则则会报无法找到类的错误。

下面我们来详细说明下Parcelable里的方法:

createFromParcel(Parcel in): 从序列化后的对象中创建原始对象。

newArray(int size): 创建指定长度的原始对象数组。

User(Parcel in): 从序列化后的对象中创建原始对象。

writeToParcel(Parcel out, int flags): 将当前对象写入序列化结构中,flags有两种情况。10,1代表当  前对象需要作为返回值返回,不能立即释放,否则返回0.几乎所有情况都为0。

describeContents(): 返回当前对象的内容描述,如果有文字标识则返回1,否则返回0.几乎所有情况都为0

Serializable和Parcelable的选择

1.选择Parcelable:Serializable是java中的序列化接口,使用起来简单,但是开销大,序列化和反序列化 过程都需要大量的IO操作。而Parcelable是Android中的序列化方式,虽然实现起来稍显麻烦,但是效率高,是 Android推荐的方式。

2.选择Serializable:如果需要将对象持久化到存储设备上或是通过网络传输到给其他客户端,推荐使用 Serializable。

此外,系统提供了许多实现了Parcelable接口的类,如Intent,Bundle,BitMap等,Map,List也可以序列化,前提是里面的每个对象可序列化。


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值