理论与实践结合-Android序列化


最近一直在各种平台搜索所谓的面试题和原理在看,但是我发现效率并不高,为什么这么说呢,编程是一项对于实践要求非常高的职业,所以是理论与实践相结合,而不是一口气吞下一个胖子。这些天,彻底走错了,应该回到自己的位置,慢慢摸索,慢慢成长,不可急于求成。

首先来了解一下序列化与反序列化。

序列化

  由于存在于内存中的对象都是暂时的,无法长期驻存,为了把对象的状态保持下来,这时需要把对象写入到磁盘或者其他介质中,这个过程就叫做序列化。

反序列化

  反序列化恰恰是序列化的反向操作,也就是说,把已存在在磁盘或者其他介质中的对象,反序列化(读取)到内存中,以便后续操作,而这个过程就叫做反序列化。

应该都知道,Android序列化有两种,一种是java生的Serializable,一种是Android生的Parcelable。

一:Serializable实现

 Serializable是java提供的一个序列化接口,它是一个空接口,专门为对象提供标准的序列化和反序列化操作,使用Serializable实现类的序列化比较简单,一般只要在类声明中实现Serializable接口即可。

如果需要实现反序列化则需声明序列化标识,否则会出现InvalidClassException,原因:

 serialVersionUID是用来辅助序列化和反序列化过程的,原则上序列化后的对象中serialVersionUID只有和当前类的serialVersionUID相同才能够正常被反序列化。

实例:

public static void main(String[] args) throws Exception {
 //把对象序列化到文件
 Client client = new Client();
 client.setId(1);
 client.setName("client");
 ObjectOutputStream out = new ObjectOutputStream
 (new FileOutputStream("/home/lckiss/test.txt"));
 out.writeObject(client);
 out.close();
 //反序列化到内存
 ObjectInputStream in = new ObjectInputStream
 (new FileInputStream("/home/lckiss/test.txt"));
 Client clientFromFile = (Client) in.readObject();
 System.out.println("Hi, My name is " + clientFromFile.getName());
 in.close();
 }

系统的默认序列化过程是可以改变的,通过实现4个方法即可以控制系统的默认序列化和反序列过程,这里就不多说了

二:parcelable实现

可以自己实现接口

也可以使用Android studio 快捷生成Parcelable代码:

 打开Setting,找到plugin插件,然后搜索Parcelable插件,找到android Parcelable code generator 安装

完成后,右键Generate,或者用快捷键Alt+insert,选择Parcelable,看到如下,点击ok即可

image.png

下面是实现后的代码,忽略json相关,我去掉了在构造函数进行原始对象的代码,所以与工具实现稍微有些不一样

import com.google.gson.annotations.SerializedName;

/**
 * Created by root on 17-8-15.
 */public class AppInfo implements Parcelable {
 //apk大小
 @SerializedName("target_size")
 private String targetSize;
 //是否强制
 private String constraint;
 //版本号
 @SerializedName("new_version")
 private String newVersion;
 //下载地址
 @SerializedName("apk_file_url")
 private String downloadUrl;
 //更新日志
 @SerializedName("update_log")
 private String updateLog;
 //MD5
 @SerializedName("new_md5")
 private String newMd5;

 /*------------------------
 get/set方法
 ------------------------*/
 //当前对象的内容描述,该方法一般返回0,仅当对象中存在文件描述符时返回1
 @Override
 public int describeContents() {
 return 0;
 }
 
 //将当前对象写入序列化结构中
 @Override
 public void writeToParcel(Parcel dest, int flags) {
 dest.writeString(this.targetSize);
 dest.writeString(this.constraint);
 dest.writeString(this.newVersion);
 dest.writeString(this.downloadUrl);
 dest.writeString(this.updateLog);
 dest.writeString(this.newMd5);
 }
 /**
 * public static final一个都不能少,内部对象CREATOR的名称也不能改变,必须全部大写。
 * 重写接口中的两个方法:
 * createFromParcel(Parcel in) 实现从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层,
 * newArray(int size) 创建一个类型为T,长度为size的数组,供外部类反序列化本类数组使用。
 **/ public static final Parcelable.Creator<AppInfo> CREATOR = new Parcelable.Creator<AppInfo>() {
 // 从序列化后的对象中创建原始对象
 @Override
 public AppInfo createFromParcel(Parcel source) {
 AppInfo appInfo=new AppInfo();
 appInfo.targetSize = source.readString();
 appInfo.constraint = source.readString();
 appInfo.newVersion = source.readString();
 appInfo.downloadUrl = source.readString();
 appInfo.updateLog = source.readString();
 appInfo.newMd5 = source.readString();

 return appInfo;
 }
 
 //创建指定长度的原始对象数组
 @Override
 public AppInfo[] newArray(int size) {
 return new AppInfo[size];
 }
 };
}

面试题:Parcelable和Serializable的区别

两者的实现差异 

  Serializable的实现,只需要实现Serializable接口即可。这只是给对象打了一个标记(UID),系统会自动将其序列化。而Parcelabel的实现,不仅需要实现Parcelabel接口,还需要在类中添加一个静态成员变量CREATOR,这个变量需要实现 Parcelable.Creator 接口,并实现读写的抽象方法。

两者的设计初衷 

  Serializable的设计初衷是为了序列化对象到本地文件、数据库、网络流、RMI以便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是由于Serializable效率过低,消耗大,而android中数据传递主要是在内存环境中(内存属于android中的稀有资源),因此Parcelable的出现为了满足数据在内存中低开销而且高效地传递问题。

两者效率选择 

  Parcelable的性能比Serializable好,在内存开销方面较小,所以Android应用程序在内存间数据传输时推荐使用Parcelable,如activity间传输数据和AIDL数据传递,而Serializable将数据持久化的操作方便,因此在将对象序列化到存储设置中或将对象序列化后通过网络传输时建议选择Serializable(Parcelable也是可以,只不过实现和操作过程过于麻烦并且为了防止android版本不同而导致Parcelable可能不同的情况,因此在序列化到存储设备或者网络传输方面还是尽量选择Serializable接口)。

两者需要注意的共同点 

  无论是Parcelable还是Serializable,执行反序列操作后的对象都是新创建的,与原来的对象并不相同,只不过内容一样。

简言之

一、对象为什么需要序列化

 1.永久性保存对象,保存对象的字节序列到本地文件。
 2.通过序列化对象在网络中传递对象。
 3.通过序列化对象在进程间传递对象。

二、当对象需要被序列化时如何选择所使用的接口

 1.在使用内存的时候Parcelable比Serializable的性能高。
 2.Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC(内存回收)。
 3.Parcelable不能使用在将对象存储在磁盘上这种情况,因为在外界的变化下Parcelable不能很好的保证数据的持续性。

相关文档:

序列化与反序列化之Parcelable和Serializable浅析

对象的序列化存储:Serializable 和 Parceable

声明:TIL|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA[ZH]协议进行授权

转载:转载请注明原文链接 - 理论与实践结合-Android序列化


Life is very interesting. In the end, some of your greatest pains become your greatest strengths.