如何将Observable中的数据包组合成一个新的Observable?

时间:2017-02-24 22:51:02

标签: android rx-java reactive-programming

我的应用程序订阅按顺序到达的Observable<Timestamped<byte[]>>个数据包,并将它们组装成更大的帧。它必须检查每个数据包以找到“帧起始”标头并进行一些小的处理以将数据包组装成有效帧。

如何创建一个新的Observable<Frame>,将这些已完成的帧发送到Subscriber

更新:建议的答案不想为我工作。一些细节:

  1. 我的来源Observable会发出Timestamped<byte[]>个数据包。
  2. 所需输出是ObservableDataFrame个对象,每个对象包括来自多个数据包的数据以及其他一些字段。
  3. 我的课程FrameAssembler的方法为DataFrame receivePacket( Timestamped<byte[]> packet )。它返回null,直到它组装了一个框架,然后它返回并为下一个框架做好准备。
  4. 我无法创建输出Observable。我正在尝试这个

    Observable<DataFrame> source = Observable
        .just( new Timestamped<byte[]>(100, new byte[10]) ) // sample packet
        .scan( new FrameAssembler(), (acc, packet) -> acc.receivePacket( packet ))  
        .filter( frame -> frame != null )
    

    但是lambda带有下划线,消息“lambda表达式中的返回类型错误:DataFrame无法转换为TestScan.FrameAssembler”。

    我完全被这个难过了。什么是acc,它在那做什么?为什么要将DataFrame返回的receivePacket转换为FrameAssembler?为什么new FrameAssembler()被用作scan()的第一个参数?

2 个答案:

答案 0 :(得分:1)

您可能希望使用双参数scan运算符:

  class ByteAccumulator {
     private byte[] buffer = ...
     public byte[] receivePacket(byte[] receivedPacket) {
        // add the received packet to the buffer
        if(containsFullFrame(buffer)) {
            return extractFrameAndTrimBuffer();
        } else {
            return null;
        }
     }
  }

  Observable<byte[]> source = ...
  source.scan(new ByteAccumulator(), ByteAccumulator::receivePacket)
        .filter(frame -> frame != null)
        ...

编辑:您需要一个中级课来调整FrameAssemblerscan期望的内容:

public FrameScanner {
  private final FrameAssembler assembler;
  private final DataFrame frame;
  public FrameScanner() {this(new FrameAssembler(), null);}
  public FrameScanner(FrameAssembler assembler,DataFrame frame) {
    this.frame=frame; this.assembler=assembler;
  }
  public getFrame() {return frame;}
  public FrameScanner scan(Timestamped<byte[]> nextBytes) {
    return new FrameScanner(assembler, assembler.receivePacker(nextBytes));
  }
}

现在你应该能够像这样使用它:

.scan(new FrameScanner(), FrameScanner::scan)
.map(FrameScanner::getFrame)
.filter(Objects::nonNull)
嗯......现在我想起来了,而不是abofethis也可能有用:

FrameAssembler assembler=new FrameAssembler();
...
.scan((DataFrame)null, (ignore, packet) -> assembler.receivePacket( packet))
.filter(Objects::nonNull)

答案 1 :(得分:0)

我无法使用scan()运算符来提供解决方案。我认为问题是在收到完整的数据包集之前返回null。可观察的运算符链似乎不喜欢空值。

我是如何解决的: 在数据包Observable subscription的onNext()处理程序中:

Thread.currentThread().setPriority( DATA_RX_PRIORITY );
packetArrayList = DataOps.addPacket( packetArrayList, dataPacket );
if( packetArrayList != null ) {  // we have a new complete packet buffer
    DataOps.DataFrame frameReturned = DataOps.pBuf2dFrame( packetArrayList );
    frameRelayer.onNext( frameReturned ); // send the new frame to the BehaviorSubject
}

addPacket()例程将每个收到的数据包添加到ArrayList,但返回null 除非累积完整的帧数据包,否则返回填充ArrayList

当收到非空ArrayList时,pBuf2dFrame()方法会解析数据包并将它们组装成一个新的DataFrame对象。 然后是将Observable数据包转换为Observable of DataFrames的技巧:frameRelayerBehaviorSubject(一个RxJava对象,可以作为Observable和Subscriber)。您所要做的就是使用新onNext()调用其DataFrame方法,将其传递给frameRelayer的任何订阅者。