2011-12-14 230 views
6

我正面临一个严重的问题,媒体播放器(MP)卡在prepare()方法。 我的应用在AsyncTask中运行prepare()以避免阻止用户界面,因为这些源来自网页。有几个用户可以随时点击的“播放”按钮,因此我在同步方法中添加了prepare()以更好地控制MP的状态。我的应用程序还会调用一个release() onPause释放使用的资源。Android MediaPlayer卡住了准备()

事情是,我注意到如果release()在准备时被调用,prepare()永远不会返回,所以我被卡在同步方法中。最糟糕的是AsyncTask线程处于死锁状态,并且每次用户在该状态下单击播放时,都会浪费另一个线程,因为它一直等待获取永不返回prepare()的显示器。很快我的所有AsyncTasks线程都被浪费了,并且由于我广泛使用它们,所以我的应用程序停止工作。

所以我的问题是:有没有人有一个如何克服这个问题的想法?我正在认真考虑重做所有使用MediaPlayer的工作,但我需要知道事先处理这种情况的最佳方法。

回答

3

您应该改用prepareAsync()。为了MediaPlayer的准备,您不需要AsyncTask

+0

我想到了这一点,但我并不确定它会解决手头的问题。 MediaPlayer文档说:“重要的是要注意,准备状态是一种暂时状态,并且在MediaPlayer对象处于准备状态时调用具有副作用的任何方法的行为未定义。”所以问题仍然存在,我会在“准备”状态中调用release(),结果是未定义的。我怀疑,在我的同步准备(),MediaPlayer转向这种“准备”状态,并导致我收到的错误。 – hgm 2011-12-14 20:59:58

0

对于这个问题,使用AsyntasksprepareAsync()的问题在于它不会自动将MediaPlayer's状态切换为准备状态。你会认为它确实如此,但由于某种原因,它不会。我陷入了同样的问题几天,直到有人建议我实施OnPreparedListener,并使用它来使用MediaPlayer

添加它非常简单。

首先,实现你的类监听器:(我使用此一服务使你看起来略有不同)

public class MusicService extends Service implements OnPreparedListener 

接下来,在您的播放/准备语句,使用prepareAsync,和设置监听器。

mp.prepareAsync(); 
mp.setOnPreparedListener(this); 

接下来,添加onPrepared方法,并添加你的出发代码:

public void onPrepared(MediaPlayer mediaplayer) { 
     // We now have buffered enough to be able to play 
     mp.start(); 
    } 

这应该做的伎俩。

0

感谢所有的答案,但我解决了这个问题,不像前面的答案中提到的那样使用prepareAsync()。我认为如果提供同步的准备方法,应该有一种使其正常工作的方法。

该修复虽然不是优雅的IMO,但很简单:避免在prepare()中调用release()。虽然媒体播放器文档中的状态图表明可以在任何状态下调用release(),但实验证明,在prepare()中调用它(大概处于“准备”状态)会挂起您的应用程序。所以我添加了一个检查,看MP是否处于这种状态,如果是,我现在正在调用release()。因为MP中没有isPreparing()方法,所以我需要在代码中添加一个布尔值来检查它。

我相信这不应该发生,并且是Android本身的错误。从评论中可以看出,文档中存在一个矛盾:它表示可以在任何状态下调用release(),但也表示在处于准备状态时调用任何状态更改命令的结果都是未定义的。希望这会帮助其他人。

0

我解决了这个问题,在我的应用程序是这样的:

创建对象的AsyncTasks(这样你就可以检查它们是否正在进行中):

private AsyncTask<String, Void, String> releaseMP; 
private AsyncTask<String, Void, String> setSource; 

为准备调用创建的AsyncTask :现在

private class setSource extends AsyncTask<String, Void, String> { 
    @Override 
    protected synchronized String doInBackground(final String... urls) { 
     try { 
      mMediaPlayer.prepare(); 
     } catch (final IllegalStateException e) { 
      e.printStackTrace(); 
      return e.getMessage(); 
     } catch (final IOException e) { 
      e.printStackTrace(); 
      return e.getMessage(); 
     } catch (final Exception e) { 
      e.printStackTrace(); 
      return e.getMessage(); 
     } 

     return null; 
    } 

    @Override 
    protected void onCancelled() { 
     if (setSource != null) 
      setSource = null; 

     // Send error to listener 
     mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 

     releaseMP = new releaseMP().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 
    } 

    @Override 
    protected void onPostExecute(final String result) { 
     if (setSource != null) 
      setSource = null; 

     // Check for error result 
     if (result != null) { 
      mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 
     } 
    } 

    @Override 
    protected void onPreExecute() { 

    } 

} 

您准备代码:

try { 
     mMediaPlayer = new MediaPlayer(); 
     mMediaPlayer.setOnPreparedListener(mPreparedListener); 
     mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); 
     mDuration = -1; 
     mMediaPlayer.setOnCompletionListener(mCompletionListener); 
     mMediaPlayer.setOnErrorListener(mErrorListener); 
     mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); 
     mCurrentBufferPercentage = 0; 
     mMediaPlayer.setDataSource(getContext(), mUri, mHeaders); 
     mMediaPlayer.setDisplay(mSurfaceHolder); 
     mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 
     mMediaPlayer.setScreenOnWhilePlaying(true); 

     // mMediaPlayer.prepareAsync(); 
     // we don't set the target state here either, but preserve the 
     // target state that was there before. 
     mCurrentState = STATE_PREPARING; 
    } catch (final IOException ex) { 
     Log.w(TAG, "Unable to open content: " + mUri, ex); 
     mCurrentState = STATE_ERROR; 
     mTargetState = STATE_ERROR; 
     mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 
     return; 
    } catch (final IllegalArgumentException ex) { 
     Log.w(TAG, "Unable to open content: " + mUri, ex); 
     mCurrentState = STATE_ERROR; 
     mTargetState = STATE_ERROR; 
     mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 
     return; 
    } catch (final Exception ex) { 
     Log.w(TAG, "Unable to open content: " + mUri, ex); 
     mCurrentState = STATE_ERROR; 
     mTargetState = STATE_ERROR; 
     mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); 
     return; 
    } 

    setSource = new setSource().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 

最后,当您需要关闭mediaPlayer时,您将在释放之前检查setSource对象以查看它是否正在准备。如果准备,你会取消的AsyncTask,并在的AsyncTask onCancelled,你会复位,发行对象:

public void release(final boolean cleartargetstate) { 
    if (mMediaPlayer != null) { 
     if (setSource != null) { 
      setSource.cancel(true); 
     } else { 
      releaseMP = new releaseMP().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); 
     } 
    } 
} 

这是我releaseMP的AsyncTask(这只是重置并释放对象):

private class releaseMP extends AsyncTask<String, Void, String> { 

    @Override 
    protected synchronized String doInBackground(final String... urls) { 
     Log.i(MethodNameTest.className() + "." + MethodNameTest.methodName(), "called"); 
     if (mMediaPlayer != null) { 
      // Release listeners to avoid leaked window crash 
      mMediaPlayer.setOnPreparedListener(null); 
      mMediaPlayer.setOnVideoSizeChangedListener(null); 
      mMediaPlayer.setOnCompletionListener(null); 
      mMediaPlayer.setOnErrorListener(null); 
      mMediaPlayer.setOnBufferingUpdateListener(null); 
      mMediaPlayer.reset(); 
      mMediaPlayer.release(); 
      mMediaPlayer = null; 
     } 
     mCurrentState = STATE_IDLE; 
     mTargetState = STATE_IDLE; 
     return null; 
    } 

    @Override 
    protected void onPostExecute(final String result) { 
     Log.i(MethodNameTest.className() + "." + MethodNameTest.methodName(), "called"); 

     if (releaseMP != null) 
      releaseMP = null; 
    } 

}