为什么要使用 Parcelable 接口:
和 Serializable 接口类似,都是为一个类实现序列化和可序列化功能,以让该类的对象可以通过 Intent 和 Binder 传递。
Parcelable 接口和 Serializable 接口的区别:
1. Serializable 接口是 java 提供的原生序列化接口,使用起来简单但是由于它的序列化和反序列化过程需要大量的 I/O 操作,所以开销很大。
2. Parcelable 接口是 Android 提供的序列化接口,因此更适合用在 Android 平台上,也是 Android 推荐的序列化方式。虽然它使用起来稍微比较麻烦但是它的效率很高,因此我们首选使用 Parcelable。(因为有 AS,其实现在也很方便,都是自动导入。)
Parcelable 接口和 Serializable 接口的使用场景:
1. Parcelable 主要用于内存序列化上,比如当我们使用 Intent 或者 Binder 传递对象时,优先使用这种方式。
2. Serializable 主要用于将对象序列化到存储设备上或者通过网络传输对象时。
Parcelable 接口使用示例:
// Girl.java 实现了 Parcelable 接口的类
package com.cfm.parcelableactivity;
public class Girl implements Parcelable {
private String mName;
private String mDream;
public Girl(String name, String dream) {
mName = name;
mDream = dream;
}
protected Girl(Parcel in) {
mName = in.readString();
mDream = in.readString();
}
public static final Creator<Girl> CREATOR = new Creator<Girl>() {
@Override
public Girl createFromParcel(Parcel in) {
return new Girl(in);
}
@Override
public Girl[] newArray(int size) {
return new Girl[size];
}
};
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
public String getDream() {
return mDream;
}
public void setDream(String dream) {
mDream = dream;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
dest.writeString(mDream);
}
}
// User.java 实现了 Parcelable 接口的类,并包含实现了 Parcelable 接口的类的对象
package com.cfm.parcelableactivity;
public class User implements Parcelable {
private int mId;
private String mName;
private String mDream;
private Girl mGirl;
public User(int id, String name, String dream, Girl girl) {
mId = id;
mName = name;
mDream = dream;
mGirl = girl;
}
/**
* 反序列化过程。从序列化后的对象中创建原始对象。
*/
private User(Parcel in) {
mId = in.readInt();
mName = in.readString();
mDream = in.readString();
mGirl = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
public static final Creator<User> CREATOR = new Creator<User>() {
/**
* 从序列化的对象中创建原始对象。也就是反序列化过程。
*/
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
/**
* 创建指定长度的原始对象数组。
*/
@Override
public User[] newArray(int size) {
return new User[size];
}
};
public int getId() {
return mId;
}
public void setId(int id) {
mId = id;
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
public String getDream() {
return mDream;
}
public void setDream(String dream) {
mDream = dream;
}
public Girl getGirl() {
return mGirl;
}
/**
* 返回当前对象的内容描述。
* @return 如果含有文件描述符,返回1;否则返回0。几乎所有情况都返回0.
*/
@Override
public int describeContents() {
return 0;
}
/**
* 将当前对象写入序列化结构中,也就是序列化过程。
* @param flags 有两种值:0或1。为 1 时标识当前对象需要作为返回值返回,不能立即释放资源;几乎所有情况都返回0。
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeString(mName);
dest.writeString(mDream);
dest.writeParcelable(mGirl, 0);
}
}
// FirstActivity.java
package com.cfm.parcelableactivity;
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.first_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("cfmtest", new User(1, "cfm", "open source", new Girl("ym", "Become Better and Better")));
startActivity(intent);
}
});
}
}
// SecondActivity.java
package com.cfm.parcelableactivity;
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
User user = getIntent().getParcelableExtra("cfmtest");
Log.d("cfmtest-SecondActivity", "mId: " + user.getId());
Log.d("cfmtest-SecondActivity", "mName: " + user.getName());
Log.d("cfmtest-SecondActivity", "mDream: " + user.getDream());
Log.d("cfmtest-SecondActivity", "mGirl.name: " + user.getGirl().getName());
Log.d("cfmtest-SecondActivity", "mGirl.dream: " + user.getGirl().getDream());
}
}
// output
cfmtest-SecondActivity: mId: 1
cfmtest-SecondActivity: mName: cfm
cfmtest-SecondActivity: mDream: open source
cfmtest-SecondActivity: mGirl.name: ym
cfmtest-SecondActivity: mGirl.dream: Become Better and Better
这里 FirstActivity 通过 Intent 将实现了 Parcelable 接口的 User 传送给 SecondActivity,可以看到 SecondActivity 成功收到并打印出了 User 对象的属性。
可以看到通过 Parcelable 接口实现对象的序列化和反序列化过程主要三步:
1. 序列化过程
由 wirteToParcel(Parcel dest, int flags) 方法完成,最终是通过 Parcel 中的一系列 write 方法来完成。(使用 AS,会自动帮我们生成并实现这个方法。)
2. 反序列化过程
由 CREATOR 来完成,其内部标明了如何创建序列化对象和数组,并通过 Parcel 的一系列 read 方法来完成反序列化过程。
3. 内容描述
由 describeContents() 方法来完成,仅当当前对象中存在文件描述符时,返回 1;其余几乎都是返回 0。
注意:我们在 User(Parcel in),也就是对象的反序列化过程调用方法中,由于 Girl 是另一个可序列化对象,所以它的反序列化过程需要传递当前线程的上下文类加载器。
mGirl =in.readParcelable(Thread.currentThread().getContextClassLoader());