Protobuf流(懒惰序列化)API

时间:2012-11-05 23:53:04

标签: android xml json model protocol-buffers

我们有一个Android应用程序,它使用Protocol Buffers来存储应用程序数据。数据格式(粗略地)是单个protobuf(“容器”),其包含作为重复字段的protobufs(“items”)列表:

message Container {
    repeated Item item = 1;
}

当我们想要保存对项目的更改时,我们必须重新创建protobuf容器,将所有项目添加到其中,然后将其序列化并将其写入文件。

这种方法的问题是它可能使保存时使用的内存增加三倍,因为数据必须首先从模型类复制到protobuf构建器,然后在protobuf序列化时复制到字节数组,所有这些都在写入之前到文件流。

我们想要的是一种创建我们的protobuf容器并将其懒惰地序列化为流的方法,然后简单地将每个protobuf项(从我们的模型数据创建)添加到容器中,该容器序列化并将其写入流,而不是保留所有项目,直到我们在内存中创建整个容器。

有没有办法构建一个protobuf并将其懒洋洋地序列化为流?

如果没有办法正式执行此操作,是否有任何图书馆可以提供帮助?有没有人有任何建议或想法如何以其他方式解决这个问题?替代数据格式或技术(例如JSON或包含protobufs的XML)可以实现这一目标吗?

3 个答案:

答案 0 :(得分:3)

序列化:

protobuf是一种可附加格式,单个项目合并,重复项目追加

因此,要将序列编写为惰性流,您需要做的就是重复编写相同的结构,列表中只有一个项目:序列化200 x“Container with 1 Item”的序列与100%完全相同序列化1 x“有200个项目的容器”。

所以:就这样做!


反序列化:

技术上非常容易作为流阅读 - 然而,这一切都归结为您正在使用的库。例如,我在protobuf-net(一个.NET / C#实现)中将其公开为Serializer.DeserializeItems<T>,它基于以下假设读取(完全延迟/流式传输)T类型的消息序列。它们采用您在问题中描述的形式(因此Serializer.DeserializeItems<Item>将是替换Serializer.Deserialize<Container>的流媒体方式 - 最外层的对象有点并不存在于protobuf中)

如果这不可用,但您可以访问原始阅读器API,那么您需要做的是:

  • 读取头部的一个varint - 这将是值10(0x0A),即分别为字段编号(1)和线路类型(2)的“(1 <&lt; 3)| 2” - 所以这也可以措辞:“从流中读取一个字节,并检查值为10”
  • 读取一个varint以获取以下项目的长度
  • 现在:
    • 如果reader API允许您限制要处理的最大字节数,请使用此长度指定后面的长度
    • 或使用长度限制流包装流API,限制为该长度
    • 或者只是手动读取那么多字节,并从有效负载构建内存中的流
  • 冲洗,重复

答案 1 :(得分:0)

没有这样的事情。 protobuf是一种包装结构。为了有效地做到这一点,它将需要所有数据。您将不得不添加&#34;流媒体协议&#34;你自己。也许每N个项目发送一个protobuf消息。

答案 2 :(得分:0)

在协议缓冲区的普通java版本中,存在一个分隔文件,您可以在其中一次编写一个协议缓冲区。我不确定它是否在Android版本

 aLocation.writeDelimitedTo(out);

正如马克所说,它很容易实施;然后写一个长度 序列化的字节。在正常(非机器人)java版本的prortocol-buffers中你也可以做(你必须序列化为字节数组或类似的东西)

private CodedOutputStream codedStream = null;


public void write(byte[] bytes) throws IOException {
    if (bytes != ConstClass.EMPTY_BYTE_ARRAY) {
        codedStream.writeRawVarint32(bytes.length);
        codedStream.writeRawBytes(bytes);
        codedStream.flush();
    }
}

    private CodedInputStream coded;

public byte[] read() throws IOException {
    if (coded == null) {
        throw new IOException("Reader has not been opened !!!");
    }
    if (coded.isAtEnd()) {
        return null;
    }
    return coded.readBytes().toByteArray();

其他协议缓冲区版本中可能存在某些内容