目录
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时标识当前对象需要作为返回值返回,不能立即释放资源,几乎所有情况都为0 | PARCELABLE_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());
}