序列化
概念
序列化:将数据结构或对象转换成二进制串的过程。
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
持久化:永久的保存对象数据(将对象数据保存在文件当中,或者是磁盘中)
广义上的序列化:数据序列化就是将数据结构或者是对象转换成我们可以存储或者传输的数据格式的一
个过程。(json、xml、protobuffer)
序列化原因:由于系统底层,数据的传输形式是字节序列。所以进程间通信、本地数据存储、网络数据传输都需要序列化。
Serializable
Serializable是java里的序列化。它只是一个标记。标记该类是可以序列化的。
serialVersionUID
(翻译)序列化版本Uid,每个类都有一个serialVersionUID,如果不去赋值,默认是Hashcode值。它标记了该对象的版本。将一个对象序列化存储到本地,然后在类里面修改几个对象,再从本地反序列化取出来,会报错serialVersionUID不对。
transient瞬时态
如果不想某参数序列化,可以声明transient。 (静态也不会被序列化)
类中所有子类都得序列化,要不然会报错 NotSerializableException
父类序列化了,继承了以后子类也序列化了
枚举比较特殊,枚举序列化存的时候就存了枚举的名字,反序列化取出来以后,通过枚举的名去差
如果父类没有序列化,那子类反序列化读取出来的时候,父类里的对象读出来为空(并且父类必须有无参的构造函数)
自定义序列化过程,主要是覆写ObjectOutputStream.writeObject()、ObjectInputStream.readObject()方法
Serializable原理
Serializable 的序列化与反序列化分别通过 ObjectOutputStream 和 ObjectInputStream 进行。
ObjectOutputStream.writeObject()
在往下就是根据各种类型,然后分开解析,String,int等,然后在写到二进制里面
ObjectInputStream.readObject()方法
Parcelable
Parcelable实现原理是Binder。
Parcelable是Android SDK提供的,它是基于内存的,由于内存读写速度高于硬盘,因此Android中的
跨进程对象的传递一般使用Parcelable。Parcel(翻译)包装。
Parcelable会将数据包装成Parcel对象,然后Binder中传输(跨进程传输数据)
Parcel提供了一套机制,可以将序列化之后的数据写入到一个共享内存中,其他进程通过
Parcel可以从这块共享内存中读出字节流,并反序列化成对象,下图是这个过程的模型。
使用
对比
反序列化后的对象会重新调用构造函数吗?
答:不会, 因为是从二进制直接解析出来的. 适用的是 Object 进行接收再强转, 因此不是原来的那个对象
序列化与反序列化后的对象是什么关系?
答:是一个深拷贝, 前后对象的引用地址不同(没关系)
Android 为什么要设计 bundle 而不是使用 HashMap 结构?
答:bundle 用的是 ArrayMap, ArrayMap 相比 Hashmap 的优点是, 扩容方便, 每次扩容是原容量的一半, 在[百量] 级别, 通过二分法查找 key 和 value (ArrayMap 有两个数组, 一个存放 key 的 hashcode, 一个存放 key+value 的 Entry) 的效率要比 hashmap 快很多, 由于在内存中或者 Android 内部传输中一般数据量较小, 因此用 bundle 更为合适
Android 中 intent/bundle 的通信原理以及大小限制?
答: Android 中的 bundle 实现了 parcelable 的序列化接口, 目的是为了在进程间进行通讯, 不同的进程共享一片固定大 小的内存, parcelable 利用 parcel 对象的 read/write 方法, 对需要传递的数据进行内存读写, 因此这一块共享内存不能 过大, 在利用 bundle 进行传输时, 会初始化一个 BINDER_VM_SIZE 的大小 = 1 * 1024 * 1024 - 4096 * 2, 即便通过 修改 Framework 的代码, bundle 内核的映射只有 4M, 最大只能扩展到 4M.
为何 Intent 不能直接在组件间传递对象而要通过序列化机制?
答: 因为 Activity 启动过程是需要与 AMS 交互, AMS 与 UI 进程是不同一个的, 因此进程间需要交互数据, 就必须序列化
Json
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。
作用
数据标记,存储,传输
特点
1.读写速度快
2.解析简单
3.轻量级
4.独立于语言,平台
5.具有自我描叙性
广义上来说Json也是序列化的一种方式。
Gson解析
- 解析原理:基于事件驱动。
DOM解析(XML解析):
优点:可以增删改查,速度快
缺点:因为一下子全部加载到内存,所以内存消耗大
基于事件驱动:
优点:内存消耗小,查询数据的速度很快
缺点:不能增删改等
- Gson使用
javaBean --> json
json --> javaBean
或者使用GsonBuilder()来创建。new 一个GsonBuilder().create();
这里使用的是构建者模式、门面模式(外观模式) 隐藏了具体复杂的实现,不想配置的参数字段,系统会给默认的配置。
比较重要的方法
自定义TypeAdapter:如果需要对解析进行特殊处理,那可以自定义TypeAdapter,主要是重写write和read方法。(其实每个对象都需要用TypeAdapter解析,系统给我们定义了一堆基础类型的TypeAdapter,至于其他的是用反射来生成TypeAdapter的,所以效率较低)
注解:
Gson原理
1. 将json串解析成 JsonElment(列表或树),然后给Adapter解析
2. 上面已经json转成列表(树)了,下面得构建他的TypeAdapter,每一个不同的JsonElment的Type就得用一个TypeAdapter,所以需要很多TypeAdapter,系统给我们定义好了通用的(基本类型的)TypeAdapter,但是别的得自己定义,如果没有自定义的TypeAdapter(见上面使用重新write,read方法),那就需要用反射来创建它。
系统给定义的TypeAdapter
String的TypeAdapter
3. 创建完TypeAdapter就去读取数据,并装入对象(Bean类里),下面看没有自定义传入Bean类,用反射创建Bean类,然后装入数据
(整体流程,new Gson().fromJson()
)
1.入参一个jsonString,一个class类型
2.进来以后根据不同的TypeToken(类型)创建不同的TypeAdapter,然后自己的走自己的
3.走反射这条路,先读出key在判断key是否支持序列化等,然后再读value
4.读完value,如果不是底层就递归,然后通过之前建的构造器,去反射填入值
4总结