找回密码
 立即注册
查看: 294|回复: 0

从xml到protobuf、flatbuffers演进之路

[复制链接]
发表于 2022-7-12 14:22 | 显示全部楼层 |阅读模式
情境


Android大文件解析
1、本次使用1.8M、92M大文件(xml、json格式各一份)作为样本数据
2、二进制文件均是通过Json序列化后的实体Bean生成,
3、依据对应关系,映射成相应的ProtoTypeAdapter(Proto)、Table (flatbuffers)实体,结合output流持久化成文件
性能简单对比


测试数据基于vivo V2080A
不同手机,花费时间量不同
相同手机,不同状态,花费时间量不同
解析时间

    xml 【使用SAX、Pull解析】159msjson 【Gson2.9.0】 79ms
  • protobuf 【Java版】 142ms
    由于对protobuf读取抱有很大的希望,目测对大文件解析会快的飞起
    但是实际情况是:protobuf的Java版解析表现并不如Gson,稍好于xml解析
    曾经一度怀疑是自己使用的方式有问题,并在官网实例的基础上扩充了数据量,其性能表现也与等量xml解析差不多
  • 插曲:https://square.github.io/wire/
    在怀疑自己使用方式的同时,找到了更友好(顺手)的API: wire
    square公司提供的api很符合开发者思维,使用起来比protoBuffer顺手多了,很愉快的就完成转换
    也实现了相关生成解析操作,其性能表现也与等量xml解析差不多

由此得出结论:
protobuf 的Java版解析大文件性能一般,不如json
猜测可能c++版会好点(c++我的没试过)

  • flatbuffers【Java版】 2ms
    由于觉得二进制不应该表现这么差,后续又摸索到flatbuffers这个库 ,
    最后验证仅仅为2ms的解析时间,后续看了它实现的原理,就不能称为解析时间了,因为它不需要解析。
    后续我又将文件大小扩容到92M,读取速度仅为47ms,这样还比1.8M的Gson解析快一倍
相关链接

Xml 解析


1、https://developer.android.com/reference/javax/xml/parsers/SAXParserFactory
2、https://developer.android.com/reference/org/xmlpull/v1/XmlPullParser
老熟人,不过多介绍了。
Json 解析

    GsonFastJson
Gson


传送门:

  • https://github.com/google/gson
    1、json格式
    2、已经对proto格式进行了支持【工程中文档没写,但是工程中已经有相关示例了】

    image.png

    能将json文本序列化成二进制proto文件
fastjson


传送门:
    https://github.com/alibaba/fastjson 老熟人了,不介绍了
  • https://github.com/alibaba/fastjson2 目标是为下一个十年提供一个高性能的JSON库
    这个库支持JSONB格式

    JSONB格式文档: https://alibaba.github.io/fastjson2/jsonb_format_cnFASTJSON v2性能有了很大提升,具体性能数据看这里:
    https://alibaba.github.io/fastjson2/benchmark_cn

有一个弊端:


image.png

只支持android 8.0以上的手机,就不多研究的,有兴趣的可以看看
Protobuf

    https://github.com/protocolbuffers/protobuf#protocol-compiler-installation proto文件编译器https://developers.google.com/protocol-buffers 语法使用
  • https://square.github.io/wire/
    今天主角是Flatbuffers,这个先跳过了
Flatbuffers

    https://google.github.io/flatbuffers/
  • https://github.com/google/flatbuffers
    这个库让人头疼的地方:
    1、就是文档很不完善,操作细节全靠摸索
    2、文件scheme和proto文件完全不一样
    3、数据流使用的是bytebuffer (转字节数组有点坑)
    4、文件使用 .fbs ,支持类型:https://google.github.io/flatbuffers/flatbuffers_guide_writing_schema.html
Built-in scalar types are  8 bit: byte (int8), ubyte (uint8), bool  16 bit: short (int16), ushort (uint16)  32 bit: int (int32), uint (uint32), float (float32)  64 bit: long (int64), ulong (uint64), double (float64)  Vector of any other type (denoted with [type]). Nesting vectors is not supported, instead you can wrap the inner vector in a table.  string, which may only hold UTF-8 or 7-bit ASCII. For other text encodings or general binary data use vectors ([byte] or [ubyte]) instead.  References to other tables or structs, enums or unions
5、fbs文件不同语言编译器:虽然文档没说,但是在release页面找到了
https://github.com/google/flatbuffers/releases 我分别下了Mac、Windows的都试过了,没啥问题
6、序列化、持久化
Flatbuffers 具体实现


1、xml.fbs文件,android studio有fbs高亮插件
namespace com.fbs.app.generated;table Menu {    type:string;    mainmenu:MainmenuDTO;}table MainmenuDTO{    item:[ItemDTO];//数组}table ItemDTO{    code:string;    activity:string;    iconum:string;    icon:string;    type:string;    extended:string;    parentid:string;    localUrl:string;    authorityHide:string;    moreentry:string;    id:string;    iconUrl:string;    homeAuth:string;    classX:string;    msgtype:string;    order:string;    appnum:string;    packageX:string;    textSize:string;    business:string;    parentflag:string;    textPostion:string;    navigateNote:string;    constructor:string;    relationId:string;    textColor:string;    url:string;    authority:string;    name:string;    textBackground:string;    hascontent:string;    DefaultLoad:string;    menuAuthority:string;    ShowTitleLine:string;    turnViewType:string;}root_type Menu;
2、根据fbs文件生成java类
下载相关系统平台的编译器,执行
$ flatc -o ./ --java animal.fbs
-o 我指定了当前目录生成,我将flatc配置成了全局变量,可以指定其他目录
--java 生成java文件,也可以指定为kotlin

生成了三个文件:Menu.java MainmenuDTO.java ItemDTO.java

3、根据json Bean生成二进制文件
private void createFlatBuffersFile(Bean bean) {FlatBufferBuilder fb = new FlatBufferBuilder(100);int[] dataArray = new int[bean.menu.mainmenu.item.size()];        for (com.example.locationapplication.Bean.MenuDTO.MainmenuDTO.ItemDTO itemDTO : bean.menu.mainmenu.item) {            int itemOffset = ItemDTO.createItemDTO(fb,                    fb.createString(itemDTO.code),                    fb.createString(itemDTO.activity),                    fb.createString(itemDTO.iconum),                    fb.createString(itemDTO.icon),                    fb.createString(itemDTO.type),                    fb.createString(itemDTO.extended),                    fb.createString(itemDTO.parentid),                    fb.createString(itemDTO.localUrl),                    fb.createString(String.valueOf(itemDTO.authorityHide)),                    fb.createString("" + itemDTO.moreentry),                    fb.createString(itemDTO.id),                    fb.createString(itemDTO.iconUrl),                    fb.createString(itemDTO.homeAuth),                    fb.createString(itemDTO.classX),                    fb.createString(itemDTO.msgtype),                    fb.createString(itemDTO.order + ""),                    fb.createString(itemDTO.appnum + ""),                    fb.createString(itemDTO.packageX),                    fb.createString(itemDTO.textSize + ""),                    fb.createString(itemDTO.business),                    fb.createString(itemDTO.parentflag + ""),                    fb.createString(itemDTO.textPostion),                    fb.createString(itemDTO.navigateNote),                    fb.createString(itemDTO.constructor),                    fb.createString(itemDTO.relationId),                    fb.createString(itemDTO.textColor),                    fb.createString(itemDTO.url),                    fb.createString(itemDTO.authority),                    fb.createString(itemDTO.name),                    fb.createString(itemDTO.textBackground),                    fb.createString(itemDTO.hascontent + ""),                    fb.createString(itemDTO.defaultLoad + ""),                    fb.createString(itemDTO.menuAuthority),                    fb.createString("ShowTitleLine"),                    fb.createString("turnViewType")            );            dataArray[index] = itemOffset;            index++;        }  int itemVector = MainmenuDTO.createItemVector(fb, dataArray); //这个文档没有说明,多试了几个create开头的方法,使用vector可行int mainmenuDTO = MainmenuDTO.createMainmenuDTO(fb, itemVector); int menuOffset = Menu.createMenu(fb,                fb.createString(bean.menu.type),                mainmenuDTO        );fb.finish(menuOffset);//根据文档来的ByteBuffer byteBuffer = fb.dataBuffer(); //到此处已经映射完成outPutTofile(byteBuffer, "/xml_flatbuffer");//打算将文件写入到包名下的files目录下,文件名为xml_flatbuffer} /** * 将解析数据持久化到文本 * @param byteBuffer* @param filename*/private void outPutTofile(ByteBuffer byteBuffer, String filename) {//        byte[] array = byteBuffer.array(); 这里有坑,直接取array 反解不出来,之前一直以为是文件存取的问题,后来发现需要使用buf.get(array);的方式,下面是正确的方式        ByteBuffer buf = byteBuffer;        byte[] array = new byte[buf.remaining()];        buf.get(array);        try {            String filePath = getFilesDir() + filename;            File file = new File(filePath);            if (!file.exists()) {                file.createNewFile();            }            FileOutputStream fileOutputStream = new FileOutputStream(file);            fileOutputStream.write(array);        } catch (IOException e) {            e.printStackTrace();        }    }读取flatbuffers文件

try {            File file = new File(getFilesDir() + "/xml_flatbuffer");            RandomAccessFile f = new RandomAccessFile(file, "r");            byte[] data = new byte[(int) f.length()];            f.readFully(data);            ByteBuffer bb = ByteBuffer.wrap(data);            Menu rootAsMenu1 = Menu.getRootAsMenu(bb);//反解结束            f.close();        } catch (IOException e) {            e.printStackTrace();        }

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-25 20:48 , Processed in 0.092480 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表