开发者

Two Runnables on the same thread ending at the same time. Is conflict possible?

Below is a simplification of some code I have for a custom View. It has a Runnable for an animation, and a Runnable for audio. The start() function starts both the animation runnable and the audio runnable. Each runnable implements an inteface with a callback on completion开发者_运维技巧.

I need to know when both are finished, so I can call 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();
      }
    }
  }
}

I've labeled a couple areas of interest with comments. What would happen if the audio and animation runnables finish at the same time? More specifically, could the callback of one runnable interrupt the callback of the other in the commented locations above? Is this possible??

I hope not, because then onBothRunnablesFinished would be called twice. If this is the case, how can I resolve this issue?


I've deleted my other answer. Now that I think about it you don't even need Runnables since the animation and media player frameworks will use their own threads. However you still need to synchronize the call to onActivityFinished() (formerly called onBothRunnablesFinished()) since the framework threads may finish simultaneously:

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

Trust me this code will work and is way cleaner. Don't even give a thought to using Runnables as Android does the multi-threading for you.

Barry


you can use an AtomicInteger initialized to 2

and finish the run with

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


I would inject a POJO mutex object into both the runnable objects and use it to synchronize both the isRunning and onCompletion methods. Acutally, isRunning does not need synchronization since it's only called from within the syncrhonized code in onCompletion, but I would consider the synchronization semantics much clearer with it also synchronized.

Using RunnableA as a template (make the changes marked // completion to both classes):

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜