Два Runnables в одном и том же потоке заканчиваются одновременно. Возможен ли конфликт? - PullRequest
0 голосов
/ 13 июля 2011

Ниже приведено упрощение кода, который я имею для пользовательского View. Он имеет Runnable для анимации и Runnable для аудио. Функция start() запускает как анимацию, так и звукозапись. Каждый выполняемый реализует интерфейс с обратным вызовом по завершении.

Мне нужно знать, когда оба будут закончены, чтобы я мог позвонить onBothRunnablesFinished().

public class Foo extends View {
  RunnableA mRunnableA;
  RunnableB mRunnableB;

  // overrides of onCreate, onMeasure, onDraw, etc...

  private void onBothRunnablesFinished() {
    // Do stuff when both runnables are finished...
  }

  public void start() {
    mRunnableA = new RunnableA();
    mRunnableB = new RunnableB();
    post(mRunnableA);
    post(mRunnableB);
  }

  private class RunnableA implements Runnable, AnimationListener {
    private MyAnimation mAnim;
    private boolean mRunning = false;

    public RunnableA() {
      mAnim = new MyAnimation();
      mAnim.setAnimationListener(this);
    }

    public boolean isRunning() {
      return mRunning;
    }

    @Override
    public void run() {
      mRunning = true;
      startAnimation(mAnim);
    }

    // Called when mAnim finishes
    @Override
    public void onAnimationEnd(Animation animation) {
      mRunning = false;
      // **WHAT IF THE OTHER RUNNABLE FINISHES NOW?**
      if (mRunnableB.isRunning() == false) {
        onBothRunnablesFinished();
      }
    }
  }

  private class RunnableB implements Runnable, OnCompletionListener {
    private MyMediaPlayer mMediaPlayer;
    private boolean mRunning = false;

    public RunnableB() {
      mMediaPlayer = MyMediaPlayer();
      mMediaPlayer.setOnCompletionListener(this);
    }

    public boolean isRunning() {
      return mRunning;
    }

    @Override
    public void run() {
      mRunning = true;
      mMediaPlayer.start();
    }

    // Called when mMediaPlayer finishes
    @Override
    public void onCompletion(MediaPlayer mp) {
      mRunning == false;
      // **WHAT IF THE OTHER RUNNABLE FINISHES NOW?**
      if (mRunnableA.isRunning() == false) {
        onBothRunnablesFinished();
      }
    }
  }
}

Я обозначил несколько областей интереса комментариями. Что произойдет, если аудио и анимация запускаются одновременно? Точнее, может ли обратный вызов одного работающего объекта прервать обратный вызов другого в указанных выше местах с комментариями? Это возможно ??

Надеюсь, что нет, потому что тогда onBothRunnablesFinished будет вызван дважды. Если это так, как я могу решить эту проблему?

Ответы [ 3 ]

2 голосов
/ 13 июля 2011

Я удалил другой ответ. Теперь, когда я думаю об этом, вам даже не нужны Runnables, так как анимация и среды медиа-плеера будут использовать свои собственные потоки. Однако вам все равно нужно синхронизировать вызов с onActivityFinished() (ранее он вызывался onBothRunnablesFinished ()), так как потоки платформы могут завершаться одновременно:

public class Foo extends View implements AnimationListener, OnCompletionListener {
    private MyAnimation mAnim;
    private MyMediaPlayer mMediaPlayer;
    private boolean mIsOneActivityFinished = false;

    synchronized private void onActivityFinished() {
        if(!mIsOneActivityFinished) {
             // The first activity is finished. Set the flag and return.
             mIsOneActivityFinished = true;
             return;
        }

        // Do stuff when both activities are finished...
    }

    public void start() {
        mAnim = new MyAnimation();
        mAnim.setAnimationListener(this);
        startAnimation(mAnim);

        mMediaPlayer = MyMediaPlayer();
        mMediaPlayer.setOnCompletionListener(this);
        mMediaPlayer.start();
    }


    // Called when mAnim finishes
    @Override
    public void onAnimationEnd(Animation animation) {
        onActivityFinished();
    }


    // Called when mMediaPlayer finishes
    @Override
    public void onCompletion(MediaPlayer mp) {
        onActivityFinished();
    }
}

Поверьте мне, этот код будет работать и будет чище. Даже не думайте об использовании Runnables, поскольку Android выполняет многопоточность.

Barry

1 голос
/ 13 июля 2011

Я бы вставил объект мьютекса POJO в оба выполняемых объекта и использовал его для синхронизации методов isRunning и onCompletion. На самом деле, isRunning не нуждается в синхронизации, так как он вызывается только из синхронизированного кода в onCompletion, но я бы посчитал семантику синхронизации гораздо более понятной, так как он также синхронизирован.

Использование RunnableA в качестве шаблона (внесите изменения, помеченные // completion для обоих классов):

private class RunnableA implements Runnable, AnimationListener {
  private MyAnimation mAnim;
  private boolean mRunning = false;
  private Object  mExecutionMutex; // completion

  public RunnableA(Object mtx) { // completion
    mAnim = new MyAnimation();
    mAnim.setAnimationListener(this);
    mExecutionMutex=mtx;
  }

  public boolean isRunning() {
    synchronized(mExecutionMutex) { // completion (Note: This is actually unnecessary)
      return mRunning;
      }
  }

  @Override
  public void run() {
    mRunning = true;
    startAnimation(mAnim);
  }

  // Called when mAnim finishes
  @Override
  public void onAnimationEnd(Animation animation) {
    synchronized(mExecutionMutex) { // completion
      mRunning = false;
      if(mRunnableB.isRunning() == false) {
        onBothRunnablesFinished();
      }
    }
  }
}
1 голос
/ 13 июля 2011

вы можете использовать AtomicInteger, инициализированный до 2

и завершите пробег с

if(atomicInt.decrementAndGet()==0)onBothRunnablesFinished();
...