序列化serializable\parcel\xml\json\protobuffer

目录

一  序列化定义

 一种用于传输对象的数据格式.

notes: 普通char类型直接传输即可.

二  序列化作用

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

三 分类

序列化分成Serializable和Parcelble 两种序列化. 前者是java的方式,后者是android特有.
实现序列化的方式: 二进制,xml,json,protobuffer.
Serializable和Parcelble 区别

  • 1、Serializable的本质是使用了反射,序列化的过程比较慢,这种机制在序列化的时候会创建很多临时的对象,比引起频繁的GC、
  • 2、Parcelable方式的本质是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的类型,这样就实现了传递对象的功能了

四 Serializable使用实例

4.1 文件,进程,网络

1) 文件读写序列化

//序列化过程
Book book = new Book(123,"Android 开发艺术探索");
FileOuputStream fos = new FileOutputStream("test.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(book);
oss.close();
//反序列化过程
FileInputStream fis = new FileInputStream("test.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Book newBook = (Book)ois.readObject();
ois.close();

2)  网络

in = new java.io.ObjectInputStream(socket.getInputStream());
Java Serializable序列化Socket传送例子  http://blog.chinaunix.net/uid-14767524-id-3399136.html

3) 进程

//MainActivity.java
Book book = new Book(123, "android开发艺术探索");
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("book",book);
startActivity(intent);
//SecondActivity.java
Intent intent = getIntent();
Book book = intent.getSerializableExtra("book");

4.2  二进制序列化,xml,json,protobuf
1) 二进制序列化

定义对象(要传输的对象)

public static class Person implements Serializable {
    private static final long serialVersionUID = 233858934995755239L;
    private String firstName;
    private String lastName;
    ......
}

二进制序列化对象.​​​​

//创建一个ObjectOutputStream输出流
outStream = new ObjectOutputStream(new FileOutputStream(filePath));
//将对象序列化到文件filePath
outStream.writeObject(person);

​2) xml 序列化

 //序列化成xml
    void XmlSerilize(TestSerilize testSerilize)
    {
        FileStream fileStream = new FileStream(Application.dataPath+"/Test.xml",FileMode.Create,FileAccess.ReadWrite,FileShare.ReadWrite);
        StreamWriter sw = new StreamWriter(fileStream, System.Text.Encoding.UTF8);
        XmlSerializer xml = new XmlSerializer(testSerilize.GetType());
        xml.Serialize(sw, testSerilize);
        sw.Close();
        fileStream.Close();
    }

xml log:

<DataTime Id="6879">
   <Date>2011-2-20 0:00:00</Date>
   <Day>20</Day>
......
</DataTime>

3)  json 序列化

var json = new { user = new { name = "fxhl", age = 23 }};
 7     string jsonData = JsonConvert.SerializeObject(json);
 8     Console.WriteLine(jsonData);

json log

{
 "timestamp": "2019-06-21T19:06:25.285730", 
 "level": "WARNING", 
  "name": "root", 
  "message": "Hello world."
}

json的解析详见:
java parse json string: https://blog.csdn.net/fdsafwagdagadg6576/article/details/79348141
GSon 实例和解析:https://blog.csdn.net/fdsafwagdagadg6576/article/details/86652218

4) protobuf 序列化

在.proto文件中定义对象。编译.protobuf生产java类,使用java类传输.

.proto文件 转换的 Java源代码 = Protocol Buffer 类 + 消息对象类(含Builder内部类)

消息对象类 是 Protocol Buffer 类的内部类

  i) 定义.proto文件

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}

 ii)  使用

// 步骤1:通过 消息类的内部类Builder类 构造 消息类的消息构造器
        Demo.Person.Builder personBuilder =  Demo.Person.newBuilder();
        // 步骤2:设置你想要设置的字段为你选择的值
        personBuilder.setName("Carson");// 在定义.proto文件时,该字段的字段修饰符是required,所以必须赋值
        personBuilder.setId(123);// 在定义.proto文件时,该字段的字段修饰符是required,所以必须赋值
        personBuilder.setEmail("carson.ho@foxmail.com"); // 在定义.proto文件时,该字段的字段修饰符是optional,所以可赋值 / 不赋值(不赋值时将使用默认值)
        Demo.Person.PhoneNumber.Builder phoneNumber =  Demo.Person.PhoneNumber.newBuilder();
        phoneNumber.setType( Demo.Person.PhoneType.HOME);// 直接采用枚举类型里的值进行赋值
        phoneNumber.setNumber("0157-23443276");
        // PhoneNumber消息是嵌套在Person消息里,可以理解为内部类
        // 所以创建对象时要通过外部类来创建
        // 步骤3:通过 消息构造器 创建 消息类 对象
        Demo.Person person = personBuilder.build();
        // 步骤4:序列化和反序列化消息(两种方式)
        /*方式1:直接 序列化 和 反序列化 消息 */
        // a.序列化
        byte[] byteArray1 = person.toByteArray();
        // 把 person消息类对象 序列化为 byte[]字节数组
        System.out.println(Arrays.toString(byteArray1));
        // 查看序列化后的字节流
       /*方式2:通过输入/ 输出流(如网络输出流) 序列化和反序列化消息 */
        // a.序列化
        ByteArrayOutputStream output = new ByteArrayOutputStream();
         try {

            person.writeTo(output);
            // 将消息序列化 并写入 输出流(此处用 ByteArrayOutputStream 代替)

        }

protobuffer封装方法:https://blog.51cto.com/9291927/2332264.
protobuffer使用:https://www.jianshu.com/p/4575342bc8ad

五  Serializable二进制,xml,json,protobuffer的区别

1) 二进制和xml的区别? 

这个问题也是为什么要用序列化。
i) 持久化
对象二进制存成文件,用户没法看懂。
ii)网络和进程传输 
二进制传输,变量没有边界符号,不知道每个域的意义。所以双方传输struct的每个item顺序必须是固定的,不利于扩展。
xml 使用xml lib传输,采用key-value方式。读写根据key就可以找到value,所以不用固定顺序,方便扩展。

2) protobuffer和json比较

protobuf优化了编码方式,造成传输更高效.
双方定义proto文件,都有message person定义,所以不用传字符串string,传序号即可。比如:传name需要4个字节,传数字1,1个字节即可。

图片引用自陶辉blog.

3)  protobuffer,json,xml 区别?

三者都是key-value. json优化了xml的树形结构,protobuffer继续优化了json的key部分. 
解析xml也麻烦,用树形结构。一层一层解析。不像protobuffer,直接变量获取。

QA 反序列化是用反射实现的

jdk1.8源码,readObject-> readObject0 -> readOrdinaryObject -> invokeReadResolve->readResolveMethod.invoke

aidl 和protobuf区别

双方使用方法有些相似,都是定义接口,自动生成java类文件,使用java类. 但是作用完全不同,protobuf类似xml是一种传输格式,而aidl类似socket是一种进程通信方式.

六  Parcel

Intent 使用parcel做组件通信: https://www.jianshu.com/p/ae1aa13a3f51

Serializable,Parcel区别?  
Serializable使用IO读写存储在硬盘上。序列化过程使用了反射技术,并且期间产生临时对象。优点代码少
Parcelable是直接在内存中读写,内存的读写速度优于硬盘读写速度,所以Parcelable序列化方式性能上要优于Serializable方式很多。网上测试速度parcel是serializable速度的10倍。缺点是能持久化存储,代码写起来相比Serializable方式麻烦一些

Parcel和binder关系? Binder都是用parcel数据传输.

实例如下:
client: intent.writeToParcel(data, 0)//Intent 写parcel; mRemote.transact //binder client 发送
server: onTransact //binder server 接收;  Intent intent = Intent.CREATOR.createFromParcel(data); //读parcel提取Intent.

Client:

public int startActivity(IApplicationThread caller, String callingPackage, ..) {
    //Parcel池中获取两个Parcel对象
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    ...
    data.writeString(callingPackage);    
    intent.writeToParcel(data, 0);//用于写入自己传递的数据
    data.writeStrongBinder(resultTo);
    mRemote.transact(START_ACTIVITY_TRANSACTION,data,reply,0);...//写入数据到data后,远程通信
}

Server:

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
    switch (code) {
    case SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION:    
        Intent intent = Intent.CREATOR.createFromParcel(data);//创建Intent。
        IBinder b = data.readStrongBinder();
        int ident = data.readInt();
        ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
        ...
        //该方法在ActivityThread中定义的ApplicationThread实体实现。
        scheduleLaunchActivity(intent, b, ident, info, ....);
        return true;
    }
}

 Parcel 原理:


扩展阅读:
Serializable是怎么一回事 https://juejin.cn/post/6850418112501268494 ----Serializable 源码分析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值