数据序列化在 Android 应用中十分重要,无论是进程间通信、本地数据存储还是网络数据传输,都离不开序列化的支持。
序列化是将数据结构或者对象装换成可用于存储或者传输的数据格式的过程,在序列化期间,数据结构或者对象将其状态信息写入到临时或者持久性存储区中,反序列化是将序列化过程中生成的数据还原成数据结构或者对象的过程。
Serializable
Serializable 是 Java 语言的特征,是醉简单的也是使用最广泛的序列化方案之一,只要实现了 Serializable 的接口的 Java 对象才可以实现序列化。这种类型的序列化是将 Java 对象转换成字节序列的过程,而反序列化则是将字节序列恢复成 Java 对象的过程。
Serializable 接口是一种标示接口,也就是无需实现方法,Java 便会对这个对象进行序列化操作。缺点是使用发射机制,在序列化的过程中会创建很多临时对象,容易出发垃圾回收,序列化的过程比较慢,对于性能要求很严格的场景不建议使用。
首先定义 Java 对象类,必须实现 Serializable 接口。序列化过程中会使用名为 serialVersionUID 的版本号和序列化的类相关联,serialVersionUID 在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类对象,如果接收者加载的对象的 serialVersionUID 和发送者加载的对象的 serialVersionUID 取值不同,则反序列化过程会出现 InvalidClassException 异常。最佳实践是显示制定 serialVersionUID 的值。在 Android Studio 中实现随机生成并导入。
-
File -> Settings... -> Editor -> Inspections -> Serialization issues -> Serializable lass without 'serialVersionUID'(选中)
- 进入实现了 Serializable 中的类,选中类名,Alt+Enter弹出提示,然后直接导入完成。
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = -3266258223378210421L;
private String userName;
private String userPwd;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
}
Parcelable
Serializable 是 Java SDK 提供的接口,序列化方式是基于磁盘或者网络的,而 Parcelable 是 Android SDK 提供的,是基于内存的,由于内存的读写速度由于磁盘,因此在 Android 中跨越进程对象的传递一般使用 Parcelable。所以在 Android 中传递数据优先使用 Parcelable。
public interface Parcelable {
/** @hide */
@IntDef(flag = true, prefix = { "PARCELABLE_" }, value = {
PARCELABLE_WRITE_RETURN_VALUE,
PARCELABLE_ELIDE_DUPLICATES,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WriteFlags {}
/**
* Flag for use with {@link #writeToParcel}: the object being written
* is a return value, that is the result of a function such as
* "<code>Parcelable someFunction()</code>",
* "<code>void someFunction(out Parcelable)</code>", or
* "<code>void someFunction(inout Parcelable)</code>". Some implementations
* may want to release resources at this point.
*/
public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
/**
* Flag for use with {@link #writeToParcel}: a parent object will take
* care of managing duplicate state/data that is nominally replicated
* across its inner data members. This flag instructs the inner data
* types to omit that data during marshaling. Exact behavior may vary
* on a case by case basis.
* @hide
*/
public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002;
/*
* Bit masks for use with {@link #describeContents}: each bit represents a
* kind of object considered to have potential special significance when
* marshalled.
*/
/** @hide */
@IntDef(flag = true, prefix = { "CONTENTS_" }, value = {
CONTENTS_FILE_DESCRIPTOR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ContentsFlags {}
/**
* Descriptor bit used with {@link #describeContents()}: indicates that
* the Parcelable object's flattened representation includes a file descriptor.
*
* @see #describeContents()
*/
public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001;
/**
* Describe the kinds of special objects contained in this Parcelable
* instance's marshaled representation. For example, if the object will
* include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
* the return value of this method must include the
* {@link #CONTENTS_FILE_DESCRIPTOR} bit.
*
* @return a bitmask indicating the set of special object types marshaled
* by this Parcelable object instance.
*/
public @ContentsFlags int describeContents();
/**
* Flatten this object in to a Parcel.
*
* @param dest The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
public void writeToParcel(Parcel dest, @WriteFlags int flags);
/**
* Interface that must be implemented and provided as a public CREATOR
* field that generates instances of your Parcelable class from a Parcel.
*/
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);
}
/**
* Specialization of {@link Creator} that allows you to receive the
* ClassLoader the object is being created in.
*/
public interface ClassLoaderCreator<T> extends 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()} and
* using the given ClassLoader.
*
* @param source The Parcel to read the object's data from.
* @param loader The ClassLoader that this object is being created in.
* @return Returns a new instance of the Parcelable class.
*/
public T createFromParcel(Parcel source, ClassLoader loader);
}
}
在 Android studio 中安装 Android Parcelable code generator 插件,然后重启 Android studio,在需要 实现的 Parcelable 右键选择 Generate -> Parcelable, 就可以自动实现 Parcelable。
import android.os.Parcel;
import android.os.Parcelable;
public class UserInfo implements Parcelable {
private String userName;
private String userPwd;
private String headImageSource;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.userName);
dest.writeString(this.userPwd);
dest.writeString(this.headImageSource);
}
public UserInfo() {
}
protected UserInfo(Parcel in) {
this.userName = in.readString();
this.userPwd = in.readString();
this.headImageSource = in.readString();
}
public static final Parcelable.Creator<UserInfo> CREATOR = new Parcelable.Creator<UserInfo>() {
@Override
public UserInfo createFromParcel(Parcel source) {
return new UserInfo(source);
}
@Override
public UserInfo[] newArray(int size) {
return new UserInfo[size];
}
};
}
实现 Parcelable 接口,需要实现的几个方法。
- describeContents:接口内容的描述,一般默认返回 0 即可。
- writeToParcel:序列化的方法,将类的数据写入到 Parcel 容器中。
- 静态的 Parcelable.Creator 接口,包含两个方法
- createFromParcel:反序列化的方法,将 Parcel 还原成 Java 对象。
- newArray:提供给外部类反序列化这个数组使用。