在Android开发过程中,因为无法将对象的引用传给Activities或者Fragments,我们需要将这些对象放到一个Intent或者Bundle里面,然后再传递。
序列化,表示将一个对象转换成可存储或可传输的状态。而序列化后的对象,可以在网络上进行传输,也可以存储到本地。
Android中Intent如果要传递类对象,可以通过两种方式实现。
1.方式一:Serializable,要传递的类实现Serializable接口传递对象
2.方式二 : Parcelable,要传递的类实现Parcelable接口传递对象。
Serializable(Java自带):
Serializable是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。
Parcelable(Android 专用):
除了Serializable之外,使用Parcelable也可以实现相同的效果,
不过不同于将对象进行序列化,Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。
我们先看Serializable的代码。弄一个实体类 Person,利用Java自带的Serializable进行序列化
public class Person implements Serializable{
private static final long serialVersionUID = 7382351359868556980L;
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在数据传递界面:
Intent intent = new Intent(MainActivity.this, SerializableActivity.class);
Person person = new Person();
person.setName("如花");
person.setAge(18);
intent.putExtra("put_ser_test", person);
startActivity(intent);
在数据接收界面:
Intent intent = getIntent();
Person per = (Person)intent.getSerializableExtra("put_ser_test");
mTvDate.setText("名字:" + per.getName() + "\n"
+ "年龄:" + per.getAge());
Serializable 到此完成,没什么好解释的。Serializable的优点就是简单。
Parcelable在数据传递的使用方面,和Serializable一模一样。在数据的接收方面,唯一的区别在于Serializable是intent.getSerializableExtra,而Parcelable是getIntent().getParcelableExtra。
它们在使用上最大的区别,是实体类:
先看代码:
public class Pen implements Parcelable{
private String color;
private int size;
// 系统自动添加,给createFromParcel里面用
protected Pen(Parcel in) {
color = in.readString();
size = in.readInt();
}
public static final Creator CREATOR = new Creator() {
/**
*
* @param in
* @return
* createFromParcel()方法中我们要去读取刚才写出的name和age字段,
* 并创建一个Person对象进行返回,其中color和size都是调用Parcel的readXxx()方法读取到的,
* 注意这里读取的顺序一定要和刚才写出的顺序完全相同。
* 读取的工作我们利用一个构造函数帮我们完成了
*/
@Override
public Pen createFromParcel(Parcel in) {
return new Pen(in); // 在构造函数里面完成了 读取 的工作
}
//供反序列化本类数组时调用的
@Override
public Pen[] newArray(int size) {
return new Pen[size];
}
};
@Override
public int describeContents() {
return 0; // 内容接口描述,默认返回0即可。
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(color); // 写出 color
dest.writeInt(size); // 写出 size
}
// ======分割线,写写get和set
//个人自己添加
public Pen() {
}
//个人自己添加
public Pen(String color, int size) {
this.color = color;
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
当你实现Parcelable接口后,Android Studio(as)就会提示你重写describeContents方法和writeToParcel方法。在writeToParcel方法中,需要写出
dest.writeString(color); // 写出 color
dest.writeInt(size); // 写出 size
写完后,发现as依旧提示报错:
你根据提示操作后,选择Add Parcelable Implementation,发现as自动添加了以下代码:
public static final Creator CREATOR = new Creator() {
/**
*
* @param in
* @return
* createFromParcel()方法中我们要去读取刚才写出的name和age字段,
* 并创建一个Person对象进行返回,其中color和size都是调用Parcel的readXxx()方法读取到的,
* 注意这里读取的顺序一定要和刚才写出的顺序完全相同。
* 读取的工作我们利用一个构造函数帮我们完成了
*/
@Override
public Pen createFromParcel(Parcel in) {
return new Pen(in); // 在构造函数里面完成了 读取 的工作
}
//供反序列化本类数组时调用的
@Override
public Pen[] newArray(int size) {
return new Pen[size];
}
};
也就是,随便一个类实现了Parcelable接口一开始就会变成这样子。Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。
使用Parcelable在代码上的确比Serializable要麻烦一些,但也没有想象中那么麻烦,因为系统已经帮我们做了很多事情。接下来,我们只需要写写我们自己需要的构造方法,写一下私有变量的get和set就行了。
整个实现Parcelable的实体类就完成了。其实,还有更简单的方法.就是使用as的android parcelable code generator插件,一键生成。如何生成的教程,网上有很多。比如这篇[点击跳转]。(http://blog.csdn.net/kroclin/article/details/40902721)
按照教程操作,生成的代码和上述手写的一模一样。如果您是第一次使用android parcelable code generator插件,我简单说一下生成步骤:
1.写出变量
private String color;
private int size;
2.点击鼠标右键:
选择Genetate,然后选择Parcelable就可。截图如下:
这里,我们将实现Parcelable接口的方法进行简单说明一下:
方法 | 功能 | 标识符 |
---|---|---|
createFromParcel(Parcel source) | 从序列化后的对象中创建原始对象 | |
newArray(int size) | 创建指定长度的原始对象数组 | |
Pen(Parcel in) | 从序列化后的对象中创建原始对象 | |
writeToParcel(Parcel out,int flags) | 将当前对象写入序列化结构中 | PARCALABLE_WRITE_RETURN_VALUE |
describeContents | 返回当前对象的内容描述,几乎所有情况都返回0,仅在当前对象中存在文件描述符时返回1 | CONTENTS_FILE_DESCRIPTOR |
Serializable的有点在于你只需要对某个类以及它的属性实现Serializable 接口即可。Serializable 接口是一种标识接口(marker interface),这意味着无需实现方法,Java便会对这个对象进行高效的序列化操作。而缺点是:这种方法是使用了反射,序列化的过程较慢。这种机制会在序列化的时候创建许多的临时对象,容易触发垃圾回收。
而Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。根据 google 工程师的说法,这些代码将会运行地特别快。原因之一就是我们已经清楚地知道了序列化的过程,而不需要使用反射来推断。同时为了更快地进行序列化,对象的代码也需要高度优化。
那到底Parcelable比Serializable快多少呢?我们来进行测试:
测试结果如下图:
由此可以得出: Parcelable 比 Serializable快了10多倍。有趣的是,即使在Nexus 10这样性能强悍的硬件上,一个相当简单的对象的序列化和反序列化的过程要花将近一毫秒。
如果您想让您开发的程序运行更流程,您需要多花点时间来实现 Parcelable ,因为这将会为你对象的序列化过程快10多倍,而且占用较少的资源。加上现在已经有了插件,实现Parcelable接口的实体类代码也可以一键生成,Parcelable 的缺点就显得微不足道了。
区别 | Serializable | Parcelable |
---|---|---|
所属API | JAVA API | Android SDK API |
原理 | 序列化和反序列化过程需要大量的I/O操作 | 序列化和反序列化过程不需要大量的I/O操作 |
开销 | 开销大 | 开销小 |
效率 | 低 | 很高 |
使用场景 | 序列化到本地或者通过网络传输 | 内存序列化 |
源码:
点击下载源码
笔者GitHub地址 点击跳转
您的start就是我分享的最大动力。
参考资料: