【问题标题】:Wait for event listeners in a loop?在循环中等待事件监听器?
【发布时间】:2016-05-14 18:58:21
【问题描述】:

我有一个控制器类,它需要对列表中的每个项目执行各种异步操作:

  • 对于每个 A
    • 播放音频并等待完成
    • 等待 t
    • 对于 A 的每个子部分 B:
      • 播放音频并等待
      • 等待 t2

当我的OnCompletionListener.onCompletion 方法被触发时,我知道音频已完成,当我的CountDownTimer.onFinish() 方法被触发时,我知道计时器何时完成。我试过返回听众:

for (final A a : getAs()) {
   show(a);
   playAudio(a.audioBegin, new MediaPlayer.OnCompletionListener() {
     @Override
     public void onCompletion(MediaPlayer mp) {
       wait(a.secs, new OnFinishListener() {
         @Override
         public void onFinish() {
           if (a.hasSubparts) {
             for (final B b : a.getBs()) {
               playAudio(b.audioBegin, new MediaPlayer.OnCompletionListener() {
               ...
       }
     }
   }
}

但最后我无法摆脱内部循环,这让我觉得整个想法都行不通。我查看了Future 界面,但这似乎也不是我想要的。

我知道我可以用一堆变量来解决这个问题,以跟踪我在状态流中的位置,但是带有一些框架的循环会更清晰。谢谢:)

【问题讨论】:

  • 您可以尝试让控制器类实现这两个接口,然后在调用 onFinish() 后使用 Iterator#next() 在下一个 A 上调用 playAudio()。
  • @fractalwrench 谢谢,但这仍然让我不知道我目前在嵌套循环中的哪个位置。我编辑了我的问题以增加一些复杂性,即在每个子部分中播放更多音频。所以你是对的,我可以轻松实现接口,但是没有一个 Iterator#next() 可以调用。
  • 不是一个真正的解决方案,但我最终创建了一个小型状态机并将状态保存在变量中。不是最干净的解决方案,所以如果有人可以提出替代方案,那就太好了。

标签: java android concurrency


【解决方案1】:

这种任务听起来是 RxJava 的好地方。

请看这个post,它向您展示了如何包装 MediaPlayer 以使用 Rx。

我会使用timer 运算符来制作您感兴趣的延迟,并使用From 运算符来逐一检查您的列表。

祝你好运

编辑:这是使用 rxJava 的一种方法,但是这是一个简化的版本,一旦你更好地理解 rxJava,你可以让它稍微好一点,例如包装回调,替换主题机制,处理错误等等开。

所以,首先我们将制作一个可播放对象的平面列表,我假设 A 和 B 对象有一个共同的父对象,因为 A 实际上比 B 有更多的方法,我假设这个结构:

private interface B {
    Object audioBegin();
    long getWaitSecs();
}

private interface A extends B {
    boolean hasSubparts();
    List<B> getBs();
}

所以为了使播放列表可以观察到,我们将这样做:

    //this will create a flat play list
    List<B> flatPlayList = new ArrayList<>();
    for ( A a : getAs()){
       flatPlayList.add(a);
       if ( a.hasSubparts()){
           for (B b : a.getBs()){
               flatPlayList.add(b);
           }
       }
    }
    //just creating an observable out of it
    Observable<B> playListObservable = Observable.from(flatPlayList);

现在我们有一个播放列表,我们需要一个接一个地播放,但是等待上一个完成+等待,为此我们将使用一个主题:

    //this will be used to inform every time an audio has completed playing after a wait
    final PublishSubject<Void> onCompleteSubject = PublishSubject.create();

并修补在一起:

//zip will send the next B on the list every time there was an audio completion
    Observable.zip(playListObservable, onCompleteSubject, new Func2<B, Void, B>() {
        @Override
        public B call(B b, Void aVoid) {
            return b;
        }
    }).subscribe(new Action1<B>() {
        @Override
        public void call(final B b) {
            //play the actual audio
            playAudio(b.audioBegin(), new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    //audio has completed playing, so we will wait sec as needed then infrom that we completed;
                    Observable.timer(b.getWaitSecs(), TimeUnit.SECONDS).subscribe(new Action1<Long>() {
                        @Override
                        public void call(Long aLong) {
                            onCompleteSubject.onNext(null);
                        }
                    });
                }
            });
        }
    });

最后,因为第一个项目在完整事件之前不会开始播放,我们将只发送一个来启动整个事件链:

onCompleteSubject.onNext(null); //will cause to start playing

【讨论】:

  • 谢谢@ndori,我以前没有听说过 RxJava。我在 RxJava 上看到 this SO post 解决了给定 here 的问题,我遇到了确切的“嵌套回调地狱”。我会进行更多调查,看看这是否有效。
  • 不,那没用。 RxJava 看起来很酷,但它的目的是监听可能在不同时间完成的多个事件。我有一个不同的问题:在循环中监听事件。
  • 添加了完整的解决方案,RxJava 学习曲线陡峭,但值得。
  • 哇,谢谢@ndori。感人的。我需要一些时间才能做到这一点,但我真诚地感谢您提供的代码。
  • 不客气,如果解决了您的问题请标记
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多