FlatBuffers

数据的序列化和反序列化

数据的序列化是程序代码里面必不可少的组成部分,当我们讨论到数据序列化的性能的时候,需要了解有哪些候选的方案,他们各自的优缺点是什么。

数据序列化的行为可能发生在数据传递过程中的任何阶段,例如网络传输,不同进程间数据传递,不同类之间的参数传递,把数据存储到磁盘上等等。通常情况下,我们会把那些需要序列化的类实现Serializable接口,但是这种传统的做法效率不高,实施的过程会消耗更多的内存。

但是我们如果使用GSON库来处理这个序列化的问题,不仅仅执行速度更快,内存的使用效率也更高。Android的XML布局文件会在编译的阶段被转换成更加复杂的格式,具备更加高效的执行性能与更高的内存使用效率。

JSON 的可读性很强,但是序列化和反序列化性能却是最差的。解析的时候,JSON 解析器首先,需要在内存中初始化一个对应的数据结构,这个事件经常会消耗 100ms ~ 200ms;解析过程中,要产生大量的临时变量,造成 Java 虚拟机的 GC 和内存抖动,解析 20KB的数据,大概会消耗 100KB 的临时内存。

三个数据序列化候选方案
  • Protocal Buffers:强大,灵活,但是对内存的消耗会比较大,并不是移动终端上的最佳选择。
  • Nano-Proto-Buffers:基于Protocal,为移动终端做了特殊的优化,代码执行效率更高,内存使用效率更佳。
  • FlatBuffers:这个开源库最开始是由Google研发的,专注于提供更优秀的性能。
    上面这些方案在性能方面的数据对比如下图所示:

可见,FlatBuffers几乎从空间和时间复杂度上完胜其他技术。

FlatBuffer

FlatBuffer是基于二进制文件的,json是基于字符串的。

FlatBuffers是一个开源的跨平台数据序列化库,可以应用到几乎任何语言(C++, C#, Go, Java, JavaScript, PHP, Python)。
最开始是 Google 为游戏或者其他对性能要求很高的应用开发的。项目地址在GitHub上。官方的文档https://github.com/google/flatbuffers。
优点
  • 直接读取序列化数据,而不需要解析(Parsing)或者解包(Unpacking):FlatBuffer 把数据层级结构保存在一个扁平化的二进制缓存(一维数组)中,同时能够保持直接获取里面的结构化数据,而不需要解析,并且还能保证数据结构变化的前后向兼容。
  • 高效的内存使用和速度:FlatBuffer 使用过程中,不需要额外的内存,几乎接近原始数据在内存中的大小。
  • 灵活:数据能够前后向兼容,并且能够灵活控制你的数据结构。
  • 很少的代码侵入性:使用少量的自动生成的代码即可实现。
  • 强数据类性,易于使用,跨平台,几乎语言无关。
缺点
  • FlatBuffers 需要生成代码,对代码有侵入性;
  • 数据序列化没有可读性,不方便 Debug;
  • 构建 FlatBuffers 对象比较麻烦,不直观,特别是如果对象比较复杂情况下需要写大段的代码;
    数据的所有内容需要使用 Schema 严格定义,灵活性不如 JSON
FlatBuffer的使用
  • 参考json
{
  "result": [
    {
      "id": "110000",
      "title": "北京市",
      "city": [
        {
          "id": "110100",
          "title": "北京市",
          "children": [
            {
              "id": "110101",
              "title": "东城区",
              "value": 1
            }
          ]
        }
      ]
    }]}
  • 定义Schema数据结构,编写address.fbs文件
table ResultList {
    result : [Result];
}

table Result {
    id : string;
    title : string;
    city : [City];
}

table City {
    id : string;
    title : string;
     children : [Children];
}

table Children {
    id : string;
    title : string;
    value:int;
    isCore:bool;
    code:long;
}

root_type ResultList;
  • 通过flatc来编译这个Schema,生成java文件(flatc工具在资料压缩包内,解压,当前目录打开cmd即可)
flatc -j -b address.fbs
  • 将生成的文件以及FlatBufferBuilder.java、Table.java放入安卓项目中(这两个文件是资料压缩包内的)
文件可能会报错,是包名以及导包的问题。
下面以其中一个文件为例。


package cn.gxh.property.Lsn13;// automatically generated, do not modify

//这个可以删掉,上面包名已经存在
package ;

import java.nio.*;
import java.lang.*;
import java.util.*;
//这个可以删掉
import com.google.flatbuffers.*;

//下面报红的话是导包的问题
public class ResultList extends Table {
  public static ResultList getRootAsResultList(ByteBuffer _bb) { return getRootAsResultList(_bb, new ResultList()); }
  public static ResultList getRootAsResultList(ByteBuffer _bb, ResultList obj) { _bb.order(ByteOrder.LITTLE_ENDIAN); return (obj.__init(_bb.getInt(_bb.position()) + _bb.position(), _bb)); }
  public ResultList __init(int _i, ByteBuffer _bb) { bb_pos = _i; bb = _bb; return this; }

  public Result result(int j) { return result(new Result(), j); }
  public Result result(Result obj, int j) { int o = __offset(4); return o != 0 ? obj.__init(__indirect(__vector(o) + j * 4), bb) : null; }
  public int resultLength() { int o = __offset(4); return o != 0 ? __vector_len(o) : 0; }

  public static int createResultList(FlatBufferBuilder builder,
      int result) {
    builder.startObject(1);
    ResultList.addResult(builder, result);
    return ResultList.endResultList(builder);
  }

  public static void startResultList(FlatBufferBuilder builder) { builder.startObject(1); }
  public static void addResult(FlatBufferBuilder builder, int resultOffset) { builder.addOffset(0, resultOffset, 0); }
  public static int createResultVector(FlatBufferBuilder builder, int[] data) { builder.startVector(4, data.length, 4); for (int i = data.length - 1; i >= 0; i--) builder.addOffset(data[i]); return builder.endVector(); }
  public static void startResultVector(FlatBufferBuilder builder, int numElems) { builder.startVector(4, numElems, 4); }
  public static int endResultList(FlatBufferBuilder builder) {
    int o = builder.endObject();
    return o;
  }
  public static void finishResultListBuffer(FlatBufferBuilder builder, int offset) { builder.finish(offset); }
};

  • 写代码
private void serialize() {
        FlatBufferBuilder builder = new FlatBufferBuilder();
        //县区
        int children1 = Children.createChildren(builder, builder.createString("1001"),
                builder.createString("海淀区"), 1, true, 10000009);
        int children2 = Children.createChildren(builder, builder.createString("1002"),
                builder.createString("朝阳区"), 2, true, 10000008);

        int[] childrens = new int[2];
        childrens[0] = children1;
        childrens[1] = children2;
        int childrenVector = City.createChildrenVector(builder, childrens);

        //市
        int city = City.createCity(builder, builder.createString("101"),
                builder.createString("北京市"), childrenVector);

        int[] cities = new int[2];
        cities[0] = city;
        int cityVector = Result.createCityVector(builder, cities);
        int result = Result.createResult(builder, builder.createString("100"),
                builder.createString("北京市1"), cityVector);

        int[] results=new int[2];
        results[0]=result;
        int resultVector = ResultList.createResultVector(builder, results);


        ResultList.startResultList(builder);
        ResultList.addResult(builder, resultVector);
        int rootResult = ResultList.endResultList(builder);
        ResultList.finishResultListBuffer(builder, rootResult);

        //-------保存数据到文件------
        File sdcard = Environment.getExternalStorageDirectory();
        File file = new File(sdcard, "address.txt");
        if (file.exists()) {
            file.delete();
        }

        ByteBuffer byteBuffer = builder.dataBuffer();
        FileOutputStream fos = null;
        FileChannel channel = null;
        try {
            fos = new FileOutputStream(file);
            channel = fos.getChannel();
            while (byteBuffer.hasRemaining()) {
                channel.write(byteBuffer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }

                if (channel != null) {
                    channel.close();
                }
            } catch (Exception e) {
            }

        }


        //------读取文件--------
        FileInputStream fis = null;
        FileChannel readChannel = null;
        try {
            fis = new FileInputStream(file);
            readChannel = fis.getChannel();
            ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
            int readBytes = 0;
            while ((readBytes = readChannel.read(byteBuffer1)) != -1) {

            }

            //把指针回到最初的状态
            byteBuffer1.flip();
            ResultList resultList = ResultList.getRootAsResultList(byteBuffer1);
            Global.showToast(resultList.result(0).id()+";"+resultList.result(0).title());
            Log.d("gxh",resultList.result(0).id()+";"+resultList.result(0).title());

        } catch (Exception e) {

        }finally {
            try {
                if (fis != null) {
                    fis.close();
                }

                if (readChannel != null) {
                    readChannel.close();
                }
            } catch (Exception e) {
            }
        }

    }

资源压缩包我已上传,若没有积分下载,可联系我留下邮箱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值