今天再来谈谈Android中的对象序列化,你了解多少呢?

序列化指的是什么?有什么用

序列化指的是讲对象变成有序的字节流,变成字节流之后才能进行传输存储等一系列操作。

反序列化就是序列化的相反操作,也就是把序列化生成的字节流转为我们内存的对象。

介绍下Android中两种序列化接口

Serializable

Java提供的一个序列化接口,是一个空接口,专门为对象提供序列化和反序列化操作。具体使用如下:

public class User implements Serializable {
private static final long serialVersionUID=519067123721561165l; private int id; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
}
}

实现Serializable接口,声明一个serialVersionUID

到这里可能有人就问了,不对啊,平时没有这个serialVersionUID啊。没错,serialVersionUID不是必须的,因为不写的话,系统会自动生成这个变量。它有什么用呢?当序列化的时候,系统会把当前类的serialVersionUID写入序列化的文件中,当反序列化的时候会去检测这个serialVersionUID,看他是否和当前类的serialVersionUID一致,一样则可以正常反序列化,如果不一样就会报错了。

所以这个serialVersionUID就是序列化和反序列化过程中的一个标识,代表一致性。不加的话会有什么影响?如果我们序列化后,改动了这个类的某些成员变量,那么serialVersionUID就会改变,这时候再拿之前序列化的数据来反序列化就会报错。所以如果我们手动指定serialVersionUID就能保证最大限度来恢复数据。

Serializable的实质其实是是把Java对象序列化为二进制文件,然后就能在进程之间传递,并且用于网络传输或者本地存储等一系列操作,因为他的本质就存储了文件。可以看看源码:


private void writeObject0(Object obj, boolean unshared)
throws IOException
{
...
try { Object orig = obj;
Class<?> cl = obj.getClass();
ObjectStreamClass desc; desc = ObjectStreamClass.lookup(cl, true); if (obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
// END Android-changed: Make Class and ObjectStreamClass replaceable.
} else if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
}
...
} private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
...
try {
desc.checkSerialize(); //写入二进制文件,普通对象开头的魔数0x73
bout.writeByte(TC_OBJECT);
//写入对应的类的描述符,见底下源码
writeClassDesc(desc, false); handles.assign(unshared ? null : obj);
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
} public long getSerialVersionUID() {
// 如果没有定义serialVersionUID,序列化机制就会调用一个函数根据类内部的属性等计算出一个hash值
if (suid == null) {
suid = AccessController.doPrivileged(
new PrivilegedAction<Long>() {
public Long run() {
return computeDefaultSUID(cl);
}
}
);
}
return suid.longValue();
}

可以看到是通过反射获取对象以及对象属性的相关信息,然后将数据写到了一个二进制文件,并且写入了序列化协议版本等等。

而获取·serialVersionUID·的逻辑也体现出来,如果id为空则会生成计算一个hash值。

Parcelable

Android自带的接口,使用起来要麻烦很多:需要实现Parcelable接口,重写describeContents(),writeToParcel(Parcel dest, @WriteFlags int flags),并添加一个静态成员变量CREATOR并实现Parcelable.Creator接口

public class User implements Parcelable {

    private int id;

    protected User(Parcel in) {
id = in.readInt();
} @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
} @Override
public int describeContents() {
return 0;
} public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
} @Override
public User[] newArray(int size) {
return new User[size];
}
}; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
}
}
  • createFromParcel,User(Parcel in) ,代表从序列化的对象中创建原始对象
  • newArray,代表创建指定长度的原始对象数组
  • writeToParcel,代表将当前对象写入到序列化结构中。
  • describeContents,代表返回当前对象的内容描述。如果还有文件描述符,返回1,否则返回0。

Parcelable的存储是通过Parcel存储到内存的,简单地说,Parcel提供了一套机制,可以将序列化之后的数据写入到一个共享内存中,其他进程通过Parcel可以从这块共享内存中读出字节流,并反序列化成对象。

这其中实际又是通过native方法实现的。具体逻辑我就没有去分析了,如果有大神朋友可以在评论区解析下。

当然,Parcelable也是可以持久化的,涉及到Parcel中的unmarshallmarshall方法。 这里简单贴一下代码:

protected void saveParce() {
FileOutputStream fos;
try {
fos = getApplicationContext().openFileOutput(TAG,
Context.MODE_PRIVATE);
BufferedOutputStream bos = new BufferedOutputStream(fos);
Parcel parcel = Parcel.obtain();
parcel.writeParcelable(new ParceData(), 0); bos.write(parcel.marshall());
bos.flush();
bos.close();
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
} protected void loadParce() {
FileInputStream fis;
try {
fis = getApplicationContext().openFileInput(TAG);
byte[] bytes = new byte[fis.available()];
fis.read(bytes);
Parcel parcel = Parcel.obtain();
parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0); ParceData data = parcel.readParcelable(ParceData.class.getClassLoader());
fis.close(); } catch (Exception e) {
e.printStackTrace();
}
}

总结

0)两者的区别,我们该怎么选择?

Serializable是Java提供的序列化接口,使用简单但是开销很大,序列化和反序列化过程都需要大量I/O操作。

Parcelable是Android中提供的,也是Android中推荐的序列化方式。虽然使用麻烦,但是效率很高。

所以,如果是内存序列化层面,那么还是建议Parcelable,因为他效率会比较高。

如果是网络传输和存储磁盘情况,就推荐Serializable,因为序列化方式比较简单,而且Parcelable不能保证,当外部条件发生变化时数据的连续性。

1)对于内存序列化方面建议用Parcelable,为什么呢?

  • 因为Serializable是存储了一个二进制文件,所以会有频繁的IO操作,消耗也比较大,而且用到了大量反射,反射操作也是耗时的。相比之下Parcelable就要效率高很多。

2)对于数据持久化还是建议用Serializable,为什么呢?

  • 首先,Serializable本身就是存储到二进制文件,所以用于持久化比较方便。而Parcelable序列化是在内存中操作,如果进程关闭或者重启的时候,内存中的数据就会消失,那么Parcelable序列化用来持久化就有可能会失败,也就是数据不会连续完整。而且Parcelable还有一个问题是兼容性,每个Android版本可能内部实现都不一样,知识用于内存中也就是传递数据的话是不影响的,但是如果持久化可能就会有问题了,低版本的数据拿到高版本可能会出现兼容性问题。 所以还是建议用Serializable进行持久化。

3)Parcelable一定比Serializable快吗?

  • 有个比较有趣的例子是:当序列化一个超级大的对象图表(表示通过一个对象,拥有通过某路径能访问到其他很多的对象),并且每个对象有10个以上属性时,并且Serializable实现了writeObject()以及readObject(),在平均每台安卓设备上,Serializable序列化速度大于Parcelable 3.6倍,反序列化速度大于1.6倍.

具体原因就是因为Serilazable的实现方式中,是有缓存的概念的,当一个对象被解析过后,将会缓存在HandleTable中,当下一次解析到同一种类型的对象后,便可以向二进制流中,写入对应的缓存索引即可。但是对于Parcel来说,没有这种概念,每一次的序列化都是独立的,每一个对象,都当作一种新的对象以及新的类型的方式来处理。

参考

Parcelable

拜拜

有一起学习的小伙伴可以关注下️我的公众号——码上积木,每天剖析一个知识点,我们一起积累知识。

Android序列化问题与思考的更多相关文章

  1. Android面试题《思考与解答》11月刊

    又来更新啦,Android面试题<思考与解答>11月刊奉上. 说说View/ViewGroup的绘制流程 View的绘制流程是从ViewRoot的performTraversals开始的, ...

  2. Android开发之漫漫长途 X——Android序列化

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  3. Android插件化的思考——仿QQ一键换肤,思考比实现更重要!

    Android插件化的思考--仿QQ一键换肤,思考比实现更重要! 今天群友希望写一个关于插件的Blog,思来想去,插件也不是很懂,只是用大致的思路看看能不能模拟一个,思路还是比较重要的,如果你有兴趣的 ...

  4. Android序列化之Serializable和Parcelable

    PS:还有几天就开学了.先来一发. 学习内容: 1.序列化的目的 2.Android中序列化的两种方式 3.Parcelable与Serializable的性能比较 4.Android中如何使用Par ...

  5. 浅谈Android序列化

    序列化原因 序列化的原因基本可以归纳为以下三种情况: 永久性保存对象,保存对象的字节序列到本地文件中: 对象在网络中传递: 对象在IPC间传递. --- --- 序列化方法 在Android系统中关于 ...

  6. Android序列化

    1.序列化的目的 (1)永久的保存对象数据(将对象数据保存在文件当中,或者是磁盘中 (2)通过序列化对象在网络中传递对象 (3)通过序列化对象在进程间传递 (4)在Intent之间,基本的数据类型直接 ...

  7. Android序列化:Parcelable/Serializable

    1.易用性及速度 1.1 Serializable——简单易用 Serializable的作用是为了保存对象的属性到本地文件.数据库.网络流.rmi以方便数据传输,当然这种传输可以是程序内的也可以是两 ...

  8. Android 序列化比对

    本文转自:https://www.zybuluo.com/linux1s1s/note/91046 注:部分内容有更改 在Android中使用序列化,无非两种途经: Parcelable 和 Seri ...

  9. android序列化(1)Parcelable与Serializable

    1.Android中实现序列化有两个选择 一是实现Serializable接口(是JavaSE本身就支持的),实现Serializable接口非常简单. 一是实现Parcelable接口(是Andro ...

随机推荐

  1. kalilinux2020.3的安装与一些坑

    1.下载镜像文件.iso kali官方下载太慢,用一些魔法也是不行,这里推荐用国内的下载源. 阿里云: https://mirrors.aliyun.com/kali-images/?spm=a2c6 ...

  2. Linux系统编程—管道

    ▋****1. 管道的概念 管道,又名「无名管理」,或「匿名管道」,管道是一种非常基本,也是使用非常频繁的IPC方式. 1.1 管道本质 管道的本质也是一种文件,不过是伪文件,实际上是一块内核缓冲区, ...

  3. AMD Ryzen 5000系列桌面处理器 2020年10月8日发布

    AMD Ryzen 5 5600X 6核心12线程,基础频率3.7GHz,最大频率4.6GHz,二级缓存3MB,三级缓存32MB,不锁频,支持DDR4 3200MHz内存,台积电7纳米工艺,PCIe ...

  4. java -inally转

    1.不管有木有出现异常,finally块中代码都会执行: 2.当try和catch中有return时,finally仍然会执行:3.finally是在return后面的表达式运算后执行的(此时并没有返 ...

  5. 多测师讲解自动化测试 _RF自定义关键字_高级讲师肖sir

    RF自定义关键字 在rf中叫关键字 在python中就叫做函数 或实例方法 我们自己可以写自定义关键字 自己创建一个库===库里面去创建模块===模块里面创建类和实例方法==>rf导入和引用 库 ...

  6. 题解:CF593D Happy Tree Party

    题解:CF593D Happy Tree Party Description Bogdan has a birthday today and mom gave him a tree consistin ...

  7. day63 Pyhton 框架Django 06

    内容回顾 1.装饰器 装饰器:是一个闭包函数,在不改变原函数的代码和调用方式的基础上,给原函数增加功能. def wrapper(func): def inner(*args,**kwargs): # ...

  8. 干货分享:用一百行代码做一个C/C++表白小程序,程序员的浪漫!

    前言:很多时候,当别人听到你是程序员的时候.第一印象就是,格子衫.不浪漫.直男.但是程序员一旦浪漫起来,真的没其他人什么事了.什么纪念日,生日,情人节,礼物怎么送? 做一个浪漫的程序给她,放上你们照片 ...

  9. 骨架屏(page-skeleton-webpack-plugin)初探

    作者:小土豆biubiubiu 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/2436173500265335 微信公众 ...

  10. Vue踩坑日记-You may use special comments to disable some warnings. Use // eslint-disable-next-line to ignore the next line. Use /* eslint-disable */ to ignore all warnings in a file.

    记录时间:2019年4月24日16:55:54 在build/webpack.base.conf.js文件中,注释或者删除掉:module->rules中有关eslint的规则