这几天在学习android知识的过程中,发现在IPC(跨进程通信)中,使用了Bundle,而Bundle的机制是使用Parcel来进行序列化,而不是使用Serializable。这令我很好奇。因为毕竟java是Android的常用语言,虽然kotlin有后来居上的发展趋势。但是还是老程序员还是对java熟悉一些,Serializable作为java的推荐的序列化方式。更应该有着较为完善的优化和策略。所以决定调研学习下,Parcelable的发展历史。
1.什么是Serializable?
Serializable是Java中的一个接口,用于标识一个类的实例可以被序列化,即可以将对象转换为字节流,以便存储到文件、数据库或在网络上传输。Serializable接口是Java序列化的基础,它没有任何方法,只是一个标记接口,用于告诉Java虚拟机该类的实例可以被序列化。
要使一个类实现Serializable接口,只需要在类的声明中添加implements Serializable
即可, 使用Serializable接口的基本步骤如下:
- 实现Serializable接口:在类的声明中添加
implements Serializable
。 - 创建对象:创建需要序列化的对象。
- 序列化对象:使用Java的ObjectOutputStream类将对象序列化为字节流,并写入文件、数据库或网络输出流中。
- 反序列化对象:使用Java的ObjectInputStream类从文件、数据库或网络输入流中读取字节流,并将其反序列化为对象。
下面是一个简单的示例,演示了如何使用Serializable接口进行对象的序列化和反序列化:
java
import java.io.*;
public class SerializationExample {
public static void main(String[] args) {
// 创建对象
MyClass objectToSerialize = new MyClass("Hello, world!");
// 序列化对象
try {
FileOutputStream fileOutputStream = new FileOutputStream("data.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(objectToSerialize);
objectOutputStream.close();
fileOutputStream.close();
System.out.println("Object serialized successfully.");
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化对象
try {
FileInputStream fileInputStream = new FileInputStream("data.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
MyClass deserializedObject = (MyClass) objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
System.out.println("Deserialized object: " + deserializedObject.getMessage());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class MyClass implements Serializable {
private String message;
public MyClass(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
2.为什么要序列化
为什么要序列化,做个类别,中文写的书籍和英文写的书籍是怎么存储在电脑中的呢? 很明显中文和英语都最终会转化为二进制编码存储于电脑中,那么二进制是一种通用的转化标准。可以类比我们使用java和python写的不同的应用程序,当想要运行在不同的平台上面时,就需要将java和python都转化为一种标准,来进行传输。我们可以理解为字节流,而序列化,就是将不同的编程语言的数据转化为统一的字节流的方式。
序列化是将对象的状态转换为可存储
或传输
的格式的过程。在软件开发中,序列化具有以下几个重要的作用:
- 持久化数据:通过序列化,可以将对象的状态保存到磁盘或者数据库中,以便在需要时进行读取和恢复。这样可以实现数据的持久化,即使程序关闭或者重启,数据仍然可以保持。
- 网络传输:在网络通信中,需要将对象从一个地方传输到另一个地方。通过序列化,可以将对象转换为字节流,在网络上传输,接收方再将字节流反序列化为对象,从而实现数据的传输。
- 跨平台通信:不同平台或者不同语言之间的通信需要将数据进行序列化和反序列化。通过采用通用的序列化格式,可以实现不同平台之间的数据交换和通信。
- 分布式系统:在分布式系统中,各个节点之间需要进行数据交换和通信。通过序列化,可以将对象转换为字节流,在分布式系统中传输,从而实现各个节点之间的数据交换和共享。
总之,序列化是实现数据持久化、网络传输、跨平台通信和分布式系统
的重要手段,它提供了一种将对象状态转换为字节流的方式,使得数据可以在不同环境和系统中进行存储、传输和共享,序列化就是将不同语言不同平台的对象转化为通用的一种流,来传输和存储。
3.Serializable和Parcelable的区别
两者最大的区别在于
-
存储媒介的不同 ,
Serializable
使用 I/O 读写存储在硬盘上 ,而Parcelable
是直接 在内存中读写 。根据我们日常使用电脑的情况就可以知道,读取内存比读取硬盘的速度要快多了,内存的读写速度通常大于 IO 读写,所以在 Android 中传递数据优先选择Parcelable
。 -
Serializable
会使用反射,序列化和反序列化过程需要大量 I/O 操作,Parcelable
自已实现封送和解封(marshalled &unmarshalled)操作不需要用反射,数据也存放在 Native 内存中,效率要快很多。
4.为什么Parcelable会比Serializable快10倍
C++提供了四种类型转换:const_cast、static_cast、dynamic_cast、reinterpret_cast。其中reinterpret_cast功能很强大。可以将对象类型指针转换为long类型的数。并且可以再次通过将该数值转换回对象指针,并可以访问对象。
js
// 在堆区分配对象
KPlayer * kPlayer = new KPlayer();
// 将对象指针转换为long类型数值
long playerValue = reinterpret_cast<long>(kPlayer);
// (在另一个位置) 将刚才的数值,转换回对象指针(要求次对象没有被手动释放过)
KPlayer * kPlayer = reinterpret_cast<KPlayer *>(playerValue );
// 调用对象的方法
kellyPlayer->show();
由此,共享内存的思路,即在Native层新建对象,并将指向对象的指针转换成数值返回Java层。Java层不同进程拿到的是相同的long类型数值,则可以访问Native层共同的对象。进而实现共享内存。
Parcel序列化的原理,既是将java数据写入共享内存的对象,在另一个位置(或进程),读取数据并反序列化成对象。
5.应该怎么选择使用Serializable和Parcelable吗
1.当内存中使用时使用Parcelable
2.当持久化存储时使用Serializable