Toc
  1. 为什么要序列化
  2. 序列化的两种方式
    1. Serializable
    2. transient 关键字
    3. Parcelable
  3. 两种序列化方式的对比
Toc
0 results found
Android 中的序列化

在 Android 中传递一些数据时,我们需要将数据序列化。序列化经常会用到 Serializable 和 Parcelable 这两个类,那么你有没有想过他们的区别是什么呢?这篇文章来分析一下各自的特点及优缺点,并对比一下两种结构的区别。

在讲它们之前,先提一个问题:为什么要序列化?

为什么要序列化

Android 开发的时候,我们时长遇到传递对象的需求,但是我们无法将对象的引用传给 Activity 或者 Fragment,我们需要将这些对象放到一个 Intent 或者 Bundle 里面,然后再传递,这时候就用到了序列化。所谓序列化就是 把 Java 对象转换为字节序列的过程 ,该字节序列可以被存储到一个储存媒介里或者在网络上进行传输;反序列化就是把 字节序列恢复为 Java 对象的过程。但是我们要知道序列化与反序列化仅处理 Java 变量而不处理方法,仅对数据进行处理。

序列化的两种方式

Android 中序列化有两种方式:Serializable 以及 Parcelable。其中 Serializable 是 Java 自带的,而 Parcelable 是 Android 专有的。

Serializable

Serializable 是 Java 提供的序列化技术。使用起来非常简单,只需要让某个 Class 实现 Serializable 就可以。在 Serializable 的文档中提出,Serializable 在序列化运行时会关联一个版本号,用 serialVersionUID 来标识,主要用来验证发送者和接收者处理的是不是同一个版本的类。如果版本不一致,则会抛出 InvalidClassException 异常。要使用这个标识,需要在类里声明如下:

private/public/protected static final long serialVersionUID = 42L;

虽然不提供这个值也行,Java 会自己生成一个该值,但是最好还是能提供一个。因为受编译器版本不同、内核版本不同等等的影响,有可能同一个类计算出来的 serialVersionUID 值不同,就会导致序列化 / 反序列化失败。

当父类实现了序列化,其子类也会自动实现序列化,不需要再显式实现 Serializable 接口了。

Seralizable 无法序列化静态变量,使用 transient 修饰的对象也无法序列化。所以,当类中有静态变量时,序列化并不会保存该变量。

transient 关键字

transient 关键字的作用是 控制变量的序列化 ,在变量声明前加上该关键字,可以 阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

实现 Serializable 时,还可以添加 writeObject()readObject()方法,虚拟机在序列化和反序列化时,会试图调用这两个方法,通过这两个方法,我们可以 控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。举例如下:

class SerializableTest implements Serializable {
private static final long serialVersionUID = 1L;

private String password = "pass";

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

private String encrypt() {
...
}

private String decrypt() {
...
}

private void writeObject(ObjectOutputStream out) {
try {
PutField putFields = out.putFields();
System.out.println("原密码:" + this.password);
this.password = encrypt(); // 加密
putFields.put("password", this.password);
System.out.println("加密后的密码" + this.password);
out.writeFields();
} catch (IOException e) {
e.printStackTrace();
}
}

private void readObject(ObjectInputStream in) {
try {
GetField readFields = in.readFields();
Object object = readFields.get("password", "");
this.password = decrypt(object.toString())
System.out.println(" 解密后的密码:" + this.password);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

Parcelable

Parcelable 是 Android 提供的序列化方案。它的出现是为了解决 Serializable 在序列化的过程中消耗资源严重的问题,但是因为本身使用需要手动处理序列化和反序列化过程,会与具体的代码绑定,使用较为繁琐,一般只获取内存数据的时候使用。

而 Parcelable 依赖于 Parcel,Parcel 的意思是包装,实现原理是在内存中 建立一块共享数据块,序列化和反序列化均是操作这一块的数据,如此来实现。如图所示:

听起来跟 Linux 的共享内存有没有很像?对,Parcelable 可以用于 Android 的进程间通信。举个简单的例子,应用的 Activity 要与 Service 通信的话,就可以使用 Parcelable。

使用 Parcelable 要稍微麻烦一些。它需要实现以下几点:

  • 类本身要实现 Parcelable 接口
  • 有一个非空的静态成员变量叫 CREATOR,并且它要实现Parcelable.Creator 接口
    • 覆写 createFromParcel(Parcel in) 方法
    • 覆写 newArray(int size) 方法
  • 覆写 describeContents() 方法
  • 覆写 writeToParcel(Parcel dest, int flags) 方法

看起来真的好麻烦。我们还是用代码来解释一下吧:

public class MyParcelable implements Parcelable {
// 要被序列化的数据
private int mData;

// 描述在这个 Parcelable 实例的中含有的特殊对象的类型。
// 例如,如果在对象的 output 中包含一个文件描述符,那它的返回值必须要包含 CONTENTS_FILE_DESCRIPTOR 的 bit 值
// 返回一个 bitmask 值
public int describeContents() {
return 0;
}

// 用二向箔把对象搞成 Parcelable
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}

// 一个必须要实现的接口,用来从一个 Parcel 创建你自定义的 Parcelable 实例
public static final Parcelable.Creator CREATOR
= new Parcelable.Creator() {

// 使用之前存储的 Parcel 来创建实例
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}

// 创建一个 Parcelable 的数组,所有元素都是 null
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};

// 从读取的数据中进行恢复
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}

两种序列化方式的对比

  1. Serializable 代码量少,Parcelable 代码量多。
  2. 在内存间传递数据的时候,Parcelable 比 Serializable 性能高、内存开销方面较小,所以推荐使用 Parcelable;而在需要保存数据到本地或者进行网络传输时,使用 Serializable。
  3. Serializable 在序列化时使用的是 反射 的技术,会产生大量的临时变量,从而引起频繁的 GC。而 Parcelable 方式的实现原理是将一个完整的对象进行 字节化 ,而 字节化 之后的每一部分都是 Intent 所支持的数据类型,这样也就实现传递对象的功能了。
打赏
支付宝
微信
本文作者:CodingRabbit
版权声明:本文首发于CodingRabbit的博客,转载请注明出处!