今天刚好看到了Intent的源码,发现它实现了Parcelable接口,突然想起了Java里面还有一个Serializable接口,于是想借此机会谈谈自己对这两个接口的看法。
在安卓里面,我们都知道无法直接把对象传给activity,为了传递对象,这些对象要么必须实现Serializable接口,要么必须实现Parcelable接口。那到底这两者有什么区别呢?我们先来看看 Serializable 接口。
1.Serializable
先看下Java源码中 Serializable 接口的定义:
package java.io;
public interface Serializable {
}
Serializable 是一个标准的Java接口,从接口定义中我们看到,它位于 java.io 包目录下,没有定义任何方法和变量,只用于确定可序列化的语义。因此我们可以实现这个接口并自己添加方法。但是序列化机制使用了反射去构造序列化对象,因此性能较慢。而且它创建了大量的临时对象,从而造成了相当大的GC,这个接口呢它比较容易实现,我们来看个例子:
class SeObject implements Serializable {
private String name;
private int age;
public SeObject(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
在这里我定义了一个 SeObject,实现了此接口,并定义了一个 name 和 age,实现了构造函数和 getter 方法。打算在activity之间传递这个 SeObject 对象,再来看看第一个activity是如何传递 SeObject 对象的:
public void startActivity(View view) {
Intent intent = new Intent(this, SecondActivity.class);
SeObject seObject = new SeObject("Leon", 28);
Bundle bundle = new Bundle();
bundle.putSerializable("Object", seObject);
intent.putExtras(bundle);
startActivity(intent);
}
首先,在第一个activity的布局里面定义了一个Button,设置 onClick 属性为 startActivity,这样的话点击Button时就会调用上面这个方法。可以看到,我是先定义了一个Bundle,将 SeObject 对象传入 putSerializable 方法,并将 bundle 对象放入 Intent,传递到 SecondActivity,来看看 SecondActivity.java 是如何获取这个 SeObject 对象的:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
SeObject seObject = (SeObject) bundle.getSerializable("Object");
Log.d("SecondActivity", "Name is " + seObject.getName() + ", age is " +
seObject.getAge());
}
打印的LOG如下:
D/SecondActivity( 7760): Name is Leon, age is 28
大家仔细对比下这两个文件的代码,很细心的同学应该会发现有一个对应关系:
putExtras -> getExtras;putSerializable -> getSerializable
在获取 SeObject 对象时,先调用 intent.getExtras() 拿到 Bundle对象,再调用 bundle.getSerializable从 bundle 对象中读取 SeObject对象,跟第一个activity中放入 SeObject 对象的顺序恰好相反,大家记住这个顺序,以后在Intent中传递自定义对象就很容易实现了,也不容易搞错。但经过自己验证,下面这种方式也能获取到 SeObject 对象:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent = getIntent();
SeObject seObject = (SeObject) intent.getSerializableExtra("Object");
Log.d("SecondActivity", "11 Name is " + seObject.getName() + ", age is " +
seObject.getAge());
}
(但是我个人还是喜欢第一种实现方式,不容易搞错,尤其是传入多个对象时,大家根据自己的习惯来就好)。
我们再来看看 Parcelable 接口。
2.Parcelable
Parcelable 接口的定义:
package android.os;
public interface Parcelable {
public void writeToParcel(Parcel dest, @WriteFlags int flags);
public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
public @ContentsFlags int describeContents();
public interface Creator<T> {
/**
* Create a new instance of the Parcelable class, instantiating it
* from the given Parcel whose data had previously been written by
* {@link Parcelable#writeToParcel Parcelable.writeToParcel()}.
*
* @param source The Parcel to read the object's data from.
* @return Returns a new instance of the Parcelable class.
*/
public T createFromParcel(Parcel source);
/**
* Create a new array of the Parcelable class.
*
* @param size Size of the array.
* @return Returns an array of the Parcelable class, with every entry
* initialized to null.
*/
public T[] newArray(int size);
}
我在这里只列出了一部分源码,从源码可以看出,Parcelable位于 android.os 包目录下,它不仅定义了一些变量和方法,还定义了一个内部接口 Creator<T>。Parcelable 过程比序列化过程快得多,其中一个原因是我们明确了序列化过程而不是使用反射来推断它,代码为此进行了大量优化。我们来看一个 Parceable 的例子:
class ParObject implements Parcelable {
private String name;
private int age;
public ParObject(String name, int age) {
this.name = name;
this.age = age;
}
public ParObject(Parcel source) {
name = source.readString();
age = source.readInt();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public static final Creator<ParObject> CREATOR = new Creator<ParObject>() {
@Override
public ParObject[] newArray(int size) {
return new ParObject[size];
}
@Override
public ParObject createFromParcel(Parcel source) {
return new ParObject(source);
}
};
}
Android 的 Parcelable 实现允许从 Parcel 中读取和写入,Parcel 在消息容器中包含了扁平的数据。如果我们想把Java对象转化成Parcelable,最好的方式就像上面那样实现Parcelable接口,并复写 writeToParcel 方法。第一步就是复写 writeToParcel 方法,将对象的所有成员写进 parcel 对象;第二步是创建一个 static 的 Parcelable.Creator 对象去实现反序列化。
public void startActivity(View view) {
ParObject parObject = new ParObject("Leon", 27);
Intent mIntent = new Intent(this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putParcelable("object", parObject);
mIntent.putExtras(bundle);
startActivity(mIntent);
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
ParObject parObject = bundle.getParcelable("object");
Log.d("SecondActivity", "the Name is " + parObject.getName() + ", age is " +
parObject.getAge());
}
打印的日志如下:
10-26 15:00:58.694 24199 24199 D SecondActivity: the Name is Leon, age is 27
仔细一看,其实跟 Serializable 的用法大同小异,唯一的区别就是把 putSerializable 换成了 putParcelable,然后从第二个activity中去取对象的时候改成了 getParcelable。
可能会有人会问了,难道就这点区别吗?当然不止,既然Android提供一个更强大的传输对象的机制,想必它还能做更多的事,那就是 Parcelable 可以传输一个包含多个对象的列表:
public void startActivity(View view) {
Intent mIntent = new Intent(this, SecondActivity.class);
Bundle bundle = new Bundle();
ArrayList<ParObject> list = new ArrayList<>(2);
list.add(new ParObject("Leon_1", 27));
list.add(new ParObject("Leon_2", 28));
bundle.putParcelableArrayList("object", list);
mIntent.putExtras(bundle);
startActivity(mIntent);
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
ArrayList<ParObject> list = bundle.getParcelableArrayList("object");
if (list != null) {
Log.d("SecondActivity", "size is " + list.size());
for (ParObject parObject : list) {
Log.d("SecondActivity", "Name is " + parObject.getName() + ", age is " +
parObject.getAge());
}
}
}
打印的日志如下:
10-26 15:12:28.023 26428 26428 D SecondActivity: size is 2
10-26 15:12:28.023 26428 26428 D SecondActivity: Name is Leon_1, age is 27
10-26 15:12:28.023 26428 26428 D SecondActivity: Name is Leon_2, age is 28
可以清楚的看到,从第一个activity传递了一个 ArrayList,第二个activity内成功获取了List。
可以看到,除了能传输 List,还能传输数组,这就是 Parcelable 比 Serializable 强大的地方。
总结如下:
1. Parcelable 相比 serializable,实现它所花费的时间要长一些,也就是写的代码要多一些
2. Serializable 更容易实现(接口内没有任何需要复写的方法)
3. Serializable 使用了Java反射,在流的会话过程中创建了大量的临时对象,因此造成了相当大的GC
4. 在Android里面可以通过 Intent 传递 Parcelable 数组
5. 在Parcelable中我们可以编写自定义代码,所以它会创建较少的垃圾对象,也是因为这个自定义实现, 它比 serializable 更快,性能更好
网友评论