5

我们有一个使用协议缓冲区来存储应用程序数据的 Android 应用程序。数据格式(大致)是单个 protobuf(“容器”),其中包含 protobuf(“items”)列表作为重复字段:

message Container {
    repeated Item item = 1;
}

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

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

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

有没有办法构建一个 protobuf 并将其延迟序列化为流?

如果没有办法正式做到这一点,是否有任何图书馆可以提供帮助?有没有人有任何建议或想法如何以其他方式解决这个问题?使这成为可能的替代数据格式或技术(例如 JSON 或包含 protobufs 的 XML)?

4

3 回答 3

5

对于序列化:

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

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

所以:就这样做吧!


对于反序列化:

从技术上讲,这很容易以流的形式阅读 - 但是,这一切都取决于您使用的是哪个库。例如,我在 protobuf-net(一个 .NET / C# 实现)中将Serializer.DeserializeItems<T>其公开T为(Serializer.DeserializeItems<Item>取代的流式传输方式也是如此Serializer.Deserialize<Container>- protobuf 中实际上并不存在最外层的对象)

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

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

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

 aLocation.writeDelimitedTo(out);

正如 Marc 指出的那样,它很容易实现;只需在序列化字节后面写一个长度。在 prortocol-buffers 的普通(非 android)java 版本中,您也可以这样做(您必须序列化为字节数组或类似的东西)

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();

在其他 Protocol-Buffers 版本中可能会发生一些事情

于 2012-11-07T21:38:12.687 回答
0

哪有这回事。protobuf 是一种压缩结构。为了有效地做到这一点,它需要所有的数据。您必须自己添加“流协议”。也许每 N 个项目发送一个 protobuf msg。

于 2012-11-05T23:57:06.483 回答