Android 序列化和反序列化

目录

 

1.什么是序列化和反序列化

2.为什么要进行序列化

2.Android中序列化的两种方式

2.1 实现 java.io.Serializable 接口

2.1.1 serialVersionUID 属性

2.2 实现 android.os.Parcelable 接口

3.Parcelable与Serializable的性能比较

4.Android中如何使用Parcelable进行序列化操作

5.Parcelable的工作原理

6.相关实例


1.什么是序列化和反序列化

序列化:是指将对象的状态信息转换为可以存储或传输的形式的过程。

反序列化:是指将序列化后的数据重新恢复成目标对象的过程。

2.为什么要进行序列化

1)、永久的保存对象数据(将对象数据保存在文件或磁盘中);

2)、使对象数据在网络上的传输(由于网络传输是以字节流的方式对数据进行传输的,因此序列化的目的是将对象数据转换成字节流的形式);

3)、使对象数据能在进程间进行传递(基础类型数据除外,对象类型数据必须进行序列化操作后才能进行传输);

4)、在Android Intent之间,基本类型的数据可直接进行传输,但是传递复杂类型数据的时候,必须进行序列化操作;

5)、序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化。

2.Android中序列化的两种方式

2.1 实现 java.io.Serializable 接口

import java.io.Serializable;

public class User implements Serializable {
    //同时在类中声明该字段
    public static final long serialVersionUID = 1L;
}

  示例代码如下

package com.jiancode.binder.bean;

import java.io.Serializable;

public class User implements Serializable {
    private String userId;
    private String userName;
    private int age;

    public User(String userId, String userName, int age) {
        this.userId = userId;
        this.userName = userName;
        this.age = age;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    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;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.jiancode.binder;

import android.util.Log;

import com.jiancode.binder.bean.User;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SerializableTest {

    public static final String TAG = Serializable.class.getSimpleName();

    @Test
    public void testSerializable(){
        User user = new User("1","test",23);
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new                     
                FileOutputStream("cache.txt"));
            oos.writeObject(user);
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testUnSerializable(){
        try {
            ObjectInputStream ois = new ObjectInputStream(new     
                FileInputStream("cache.txt"));
            User user = (User) ois.readObject();
            Log.e(TAG,user.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

我们可以看到,序列化成功后,文件中保存的数据是一堆乱码

 反序列话口得到的数据如下,

那么二者究竟是不是同一个对象呢?

@Test
    public void testEquals(){
        User user = new User("1","test",23);
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cache.txt"));
            oos.writeObject(user);
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        User user2 = null;
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cache.txt"));
             user2= (User) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.err.println(user);
        System.err.println(user2);
    }

从上图结果中我们可以看出,二者并不是同一个对象,其实原理很简单,对象序列化完成后,被垃圾回收期回收,在内存中被释放,反序列化出来的对象是一个新的对象,只不过其属性值和原来的对象是一样的。

2.1.1 serialVersionUID 属性

刚开始提到,不指定serialVersionUID也可以完成序列化,那么到底要不要指定呢?如果指定的话,其后面的值又表示什么意思呢?其实,这个serialVersionUID是用来辅助序列化和反序列化的,原则上只有序列化后的数据中serialVersionUID和当前类的serialVersionUID是一致的,才能被正常的反序列化。

serialVersionUID的详细工作机制是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化文件中,看它时候和当前类的serialVersionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候就可以成功反序列化;否则就说明当前类和序列话的类相比发生了该变,比如成员变量的数量,类型发生了改变,这个时候是无法被反序列化的

一般情况下,我们应该手动指定该值,比如1L,如果不手动指定,比如增加或者删除了某些变量,那么系统会重新计算当前类的hashCode并把它赋值给serialVersionUID,这样反序列化就会失败。还有一种特殊的情况,如果类的结构发生了非常规的改变,比如修改了类名或者修改类的属性,即使serialVersionUID相同,也会反序列化失败。

2.2 实现 android.os.Parcelable 接口

Parcelable也是一个接口,只要实现了该接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递,下面的示例是一个典型的用法。

package com.jiancode.binder.bean;

import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {
    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;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookId=" + bookId +
                ", bookName='" + bookName + '\'' +
                '}';
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    public static final Creator<Book> CREATOR = new Parcelable.Creator<Book>(){

        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel in){
        bookId = in.readInt();
        bookName = in.readString();
    }
}

这里先说下Parcel,Parcel内部包装了可序列化的数据,可以在Binder中自由传输。从上面的代码可以看出,在序列化的过程中需要实现的功能有序列化、反序列化和内容描述。序列化的功能有writeToParcel方法来完成,最终是通过Parcel中的一系列write方法来完成的;反序列化有CREATOR来完成,其内部标明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化操作;内容描述功能有describeContents方法来完成,几乎所有情况下这个方法都应该返回0,当且仅当对象中存在文件描述符时,此方法返回1。

Parcelable的方法说明
方法功能标记位
createFromParcel(Parcel in)从序列化后的对象中创建原始对象 
new Array(int size)创建指定长度的原始对象数组 
Book(Parcel in)从序列化后的对象中创建原始对象 
writeToParcel(Parcel out,int flags)将当前对象写入序列化结构中,其中flags标识有两种:0或1。为1时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0PARCELABLE_WRITE_RETURN_VALUE
describeContents返回当前对象的内容描述,如果含有文件描述符,返回一,否则返回0,几乎所有的情况都返回0。CONTENTS_FILE_DESCRIPTOR

3.Parcelable与Serializable的性能比较

首先Parcelable的性能要强于Serializable的原因我需要简单的阐述一下

  1). 在内存的使用中,前者在性能方面要强于后者

  2). 后者在序列化操作的时候会产生大量的临时变量,(原因是使用了反射机制)从而导致GC的频繁调用,因此在性能上会稍微逊色

  3). Parcelable是以Ibinder作为信息载体的.在内存上的开销比较小,因此在内存之间进行数据传递的时候,Android推荐使用Parcelable,既然是内存方面比价有优势,那么自然就要优先选择.

  4). 在读写数据的时候,Parcelable是在内存中直接进行读写,而Serializable是通过使用IO流的形式将数据读写入在硬盘上.

  但是:虽然Parcelable的性能要强于Serializable,但是仍然有特殊的情况需要使用Serializable,而不去使用Parcelable,因为Parcelable无法将数据进行持久化,因此在将数据保存在磁盘的时候,仍然需要使用后者,因为前者无法很好的将数据进行持久化.(原因是在不同的Android版本当中,Parcelable可能会不同,因此数据的持久化方面仍然是使用Serializable)

4.Android中如何使用Parcelable进行序列化操作

可参考上面的额Book示例

5.Parcelable的工作原理

更新中......

6.相关实例

第一个Activity

   private void startSencondActivity() {
        Book book = new Book(1,"2");
        Bundle bundle = new Bundle();
        bundle.putParcelable("book",book);
        Intent intent = new Intent(this,SecondActivity.class);
        intent.putExtras(bundle);
        startActivity(intent);
    }

第二个Activity

 private void getData(){
        Bundle bundle = getIntent().getExtras();
        Book book = bundle.getParcelable("book");
        Log.e(TAG,book.toString());
    }

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值