一、背景
我们做软件开发的应该知道,系统将内存分为用户空间和内核空间。内核空间能够与所有用户空间进行直接通信,而用户空间与用户空间之间是无法进行直接通信。于是为了用户空间与用户空间之间通信,进程间通信技术(IPC)应运而生了。但是系统底层只认识字节序列,而Java是面向对象的,于是序列化与反序列化成了解决方案。
二、序列化定义
- 由于在系统底层,数据的传输形式是简单的字节序列形式传递,即在底层系统不认识对象,只认识字节序列(二进制串),而为了达到进程通讯的目的,需要先将数据序列化,而序列化就是将对象转化字节序列的过程。相反地,当字节序列被传递到相应的进程的时候,该进程为了识别这些数据,就要将其反序列化,即把字节序列转化为对象
- 无论是在进程间通信、本地数据存储又或者是网络数据传输都离不开序列化的支持。而针对不同场景选择合适的序列化方案对于应用的性能有着极大的影响。
- 从广义上讲,数据序列化就是将数据结构或者是对象转换成我们可以存储或者传输的数据格式的一个过程,在序列化的过程中,数据结构或者对象将其状态信息写入到临时或者持久性的存储区中,而在对应的反序列化过程中,则可以说是生成的数据被还原成数据结构或对象的过程。
- 这样来说,数据序列化相当于是将我们原先的对象序列化概念做出了扩展,在对象序列化和反序列化中,我们熟知的有两种方法,其一是Java语言中提供的Serializable接口,其二是Android提供的Parcelable接口。而在这里,因为我们对这个概念做出了扩展,因此也需要考虑几种专门针对数据结构进行序列化的方法,如现在那些个开放API一般返回的数据都是JSON格式的,又或者是我们Android原生的SQLite数据库来实现数据的本地存储,从广义上来说,这些都可以算做是数据的序列化
三、反序列化定义
将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程叫做反序列化。
如今计算机语言千千万,于是在不同的计算机语言中数据结构,对象以及二进制串的表示方式并不相同。
(1)Java语言中
数据结构和对象:对于类似 Java 这种完全面向对象的语言,工程师所操作的一切都是对象(Object),来自于类的实例化。在 Java 语言中最接近数据结构的概念,就是 POJO(Plain Old Java Object)或者 Javabean也就是那些我们经常定义的只有 setter/getter 方法的类。
二进制串:二进制串在 Java 里面所指的是 byte[],byte 是 Java 的 8 中原生数据类型之一(Primitive data types)。在 Java 语言里面,二进制串的概念容易和 String 混淆。实际上 String 是 Java 的一等公民,是一种特殊对象(Object)。
(2)C语言中
数据结构:C语言是面向过程的编程语言,所以它没有对象的概念。而数据结构在C语言中表现为我们最常见最常听的数组、队列、链表等等这些结构体。
二进制串:而在 C语言中 序列化所生成的二进制串指的是存储在内存中的一块原始数据。C 语言的字符串可以直接被传输层使用,因为其本质上就是以'0'结尾的存储在内存中的二进制串,这与Java的String字符串是有很大区别的。
对于跨语言间的通讯,序列化后的数据当然不能是某种语言的特殊数据类型。所以最终都会转换成一种只有计算机底层系统唯一认可的二进制串来进行数据传输。
四、序列化/反序列化的目的
- 序列化: 主要用于网络传输,数据持久化,一般序列化也称为编码(Encode)
- 反序列化: 主要用于从网络,磁盘上读取字节数组还原成原始对象,一般反序列化也称为解码(Decode)
具体的讲:
- 永久的保存对象数据(将对象数据保存在文件当中,或者是磁盘中),这样就不会因为内存回收而丢失重要的数据了(这也是我们常说的三级缓存机制中磁盘缓存实现的基础)。
- 通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的.因此序列化的目的是将对象数据转换成字节流的形式。学过网络编程的同学可能知道,这其实就是我们所学的字节序转换的基本原理,当然网络编程里面还有无数的知识点,如大端小端,分包、拆包等等。这些都是一个合格的开发人员需要学习的内容。好难啊!学不动。)
- 将对象数据在进程之间进行传递(Activity之间传递对象数据时,需要在当前的Activity中对对象数据进行序列化操作.在另一个Activity中需要进行反序列化操作讲数据取出,可能有人就会狠疑惑了,同一个应用的两个Activity一般都是在一个进程中运行的才对啊,为什么同一个进程中的两个Activity之间传递数据还要序列化呢?这个问题我们将在接下来的讲解中为大家一步步解惑)。
- Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即这些对象的生命周期不会比JVM的生命周期更长(即每个对象都在JVM中)但在现实应用中,就可能要停止JVM运行,但有要保存某些指定的对象,并在将来重新读取被保存的对象。这时Java对象序列化就能够实现该功能。(可选择入数据库、或文件的形式保存)
- 序列化对象的时候只是针对变量进行序列化,不针对方法进行序列化。这就话什么意思呢?意思就是Java中的对象都会有一个个的方法,而进行序列化的时候这些方法并不在序列化的范围内,只会序列化该对象的变量。还有一点需要注意的就是,我们所说的序列化针对的都是一个个对象实列而不是Java中的类。
- 在Intent之间,基本的数据类型直接进行相关传递即可,但是一旦数据类型比较复杂的时候,就需要进行序列化操作了.
五、几种常见的序列化和反序列化协议
(1)、XML&SOAP
XML 是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点,SOAP(Simple Object Access protocol) 是一种被广泛应用的,基于 XML 为序列化和反序列化协议的结构化消息传递协议。但是对于Android开发人员来说似乎很少遇到这种用XML来进行序列化然后传输数据的情况。我而是在第一家公司基于pjsip进行音视频开发时接触过一点点,直到现在我对这种协议依旧是一知半解的状态。这里我们就简单的来讲解一下这个序列化协议,以免日后再次遇到时依旧一脸懵逼的尴尬局面。
XML,即 extensible Markup Language ,是一种数据标记语言 & 传输格式。它能够实现对数据进行标记(结构化数据)、存储 & 传输。它的主要特性和有点如下:
![](/qrcode.jpg)
- 灵活性: 可自定义标签,文档结构
- 自我描叙性
-
- XML文档即 一个纯文本文件,代码结构清晰,适合人类阅读
- 有文本处理能力的软件都可以处理XML
- 可扩展性: 可在不中断解析,应用程序的情况下进行扩展
- 可跨平台数据传输: 可以不兼容的系统间交换数据,降低了复杂性
- 数据共享: XML 以纯文本进行存储,独立于软硬件和应用程序的数据存储方式,使得不同的系统都能访问XML
XML语言有个一目了然的语言结构——树形结构,如下面代码所示,我们能够通过代码一眼就能够很清楚的理清楚层次关系。
<?xml version="1.0" encoding="utf-8"?>
<classes><!--根节点 -->
<student id="0">
<name>Av</name>
<age>23</age>
<sax>男</sax>
<Courses>
<course name="语文" score="90"/>
<course name="数学" score="78"/>
</Courses>
</student>
<student id="1">
<name>Lance</name>
<age>22</age>
<sax>男</sax>
<Courses>
<course name="语文" score="59"/>
<course name="数学" score="38"/>
</Courses>
</student>
</classes>
如果还是理不清,没关系,我们来看看结构图,如下:
XML文件是由节点构成的。它的第一个节点为“根节点”。一个XML文件必须有且只能有一个根节点,其他节点都必须是它的子节点,每个子节点又可以有自己的子节点。
那么在开发中如何解析上面这段xml呢?我们同样来看解析代码:
public void domTest(Context context) {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(context.getResources().openRawResource(R.raw.students));
//通过Document对象的getElementsByTagName()返根节点的一个list集合
NodeList studentList = document.getElementsByTagName("student");
for (int i = 0; i < studentList.getLength(); i++) {
Student student = new Student();
//循环遍历获取每一个student
Node studentNode = studentList.item(i);
if (((Element) studentNode).hasAttribute("id")) {
student.setId(Integer.parseInt(((Element) studentNode).getAttribute("id")));
}
//解析student节点的子节点
NodeList childList = studentNode.getChildNodes();
for (int t = 0; t < childList.getLength(); t++) {
//区分出text类型的node以及element类型的node
if (childList.item(t).getNodeType() == Node.ELEMENT_NODE) {
if (childList.item(t).getNodeName().equalsIgnoreCase("Courses")) {
NodeList courses = childList.item(t).getChildNodes();
for (int j = 0; j < courses.getLength(); j++) {
Node courseNode = courses.item(j);
if (courseNode.getNodeType() != Node.ELEMENT_NODE) {
continue;
}
NamedNodeMap namedNodeMap = courseNode.getAttributes();
Course course = new Course();
student.addCourse(course);
for (int k = 0; k < namedNodeMap.getLength(); k++) {
Node courseAttr = namedNodeMap.item(k);
if (courseAttr.getNodeName().equals("name")) {
course.setName(courseAttr.getNodeValue());
} else if (courseAttr.getNodeName().equals("score")) {
course.setScore(Float.parseFloat(courseAttr.getNodeValue()));
}
}
}
} else {
Node child = childList.item(t);
if (child.getNodeName().equals("name")) {
student.setName(child.getTextContent());
} else if (child.getNodeName().equals("age")) {
student.setAge(Integer.parseInt(child.getTextContent()));
} else if (child.getNodeName().equals("sax")) {
student.setSax(child.getTextContent());
}
}
}
}
Log.i("Zero", "解析完毕: " + student);
}
} catch (ParserConfigurationException | SAXException | IOException e) {
Log.e("Zero", e.getMessage());
}
}
(2)、JSON(Javascript Object Notation)
JSON 起源于弱类型语言 Javascript, 它的产生来自于一种称之为"Associative array"的概念,其本质是就是采用"Attribute-value"的方式来描述对象。实际上在 Javascript 和 PHP 等弱类型语言中,类的描述方式就是 Associative array。JSON 的如下优点,使得它快速成为最广泛使用的序列化协议之一。这种协议我想所有做软件开发的尤其是做Android或者后台开发的朋友都再熟悉不过啦。因为现在几乎所有网络请求的数据集都是通过这种协议返回的。
- 这种 Associative array 格式非常符合工程师对对象的理解。
- 它保持了 XML 的人眼可读(Human-readable)的优点。
- 相对于 XML 而言,序列化后的数据更加简洁。 来自于的以下链接的研究表明:XML 所产生序列化之后文件的大小接近 JSON 的两倍
- 它具备 Javascript 的先天性支持,所以被广泛应用于 Web browser 的应用常景中,是 Ajax 的事实标准协议。
- 与 XML 相比,其协议比较简单,解析速度比较快。
- 松散的 Associative array 使得其具有良好的可扩展性和兼容性
(3)、Protobuf
Protobuf 具备了优秀的序列化协议的所需的众多典型特征。但是在Android软件开发中一般很少有人会用到,所以这里就不讲了,主要是本人也学的不好。
- 标准的 IDL 和 IDL 编译器,这使得其对工程师非常友好。
- 序列化数据非常简洁,紧凑,与 XML 相比,其序列化之后的数据量约为 1/3 到 1/10。
- 解析速度非常快,比对应的 XML 快约 20-100 倍。
- 提供了非常友好的动态库,使用非常简介,反序列化只需要一行代码。