Android MediaPlayer stuck in preparation mode ()

I'm having a serious problem with Media Player (MP) getting stuck in the prepare() method. My application runs prepare() in AsyncTask to avoid blocking the user interface since the sources are from the Internet. There are several play buttons that the user can click at any time, so I added prepare() inside the synchronized method to better control MP status. My application also calls release() onPause to free up used resources.

Thing is, I noticed that if release() is called during preparation, prepare() never returns, and so I am stuck inside a synchronized method. The worst part is that the AsyncTask thread is at a standstill, and every time the user clicks on playback in this state, another thread is wasted as he continues to wait to get a monitor that is in possession of the never return prepare() . Soon, all my AsyncTasks threads are wasted, and since I use them extensively, my application stops working.

So my question is: does anyone have an idea on how to overcome this problem? I am seriously thinking about redoing all my work with MediaPlayer, but I need to know how best to deal with such situations in advance.

+6
source share
4 answers

Instead, use prepareAsync() . And you won't need AsyncTask just to prepare MediaPlayer .

+3
source

The problem with using Asyntasks or prepareAsync() in this regard is that it does not automatically switch MediaPlayer's state to prepared. You think this is so, but for some reason it is not. I got stuck in the same problem for several days until someone suggested I implement OnPreparedListener and use it to use MediaPlayer .

Adding is pretty simple.

First, implement a listener in your class: (I use this for service, so yours will look a little different.)

 public class MusicService extends Service implements OnPreparedListener 

Then, in the instructions for playback / preparation, use prepareAsync and install the listener.

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

Then add the onPrepared method and add the start code:

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

That should do the trick.

+1
source

Thanks for all the answers, but I fixed it without using prepareAsync (), as mentioned in previous answers. I think that if a synchronous preparation method were prepared, there must be a way to make it work correctly.

The fix, though not elegant IMO, is simple: avoid calling release () inside the prepare () command. Although the state diagram in the Media Player docs says that release () can be called in any state, the experiment showed that calling it inside the prepare () method (presumably in the Prep state) will hang your application. So I added a check to find out if the MP is in this state, and now I run the release release () call, if so. I needed to add a boolean value in my code in order to check this, since there is no isPreparing () method in MP.

I am sure that this should not happen, and this is a mistake in Android itself. As can be seen from the comments, there is a contradiction in the documents: it says that release () can be called in any state, but also says that invoking any state-changing commands, while in the prepared state, has the result undefined. Hope this helps other people.

0
source

I solved this problem in my application as follows:

Create objects for AsyncTasks (so you can check if they are running):

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

Create an AsyncTask to invoke preparation:

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

Now your preparation code:

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

And finally, when you need to kill mediaPlayer, you check the setSource object to see if it is prepared before it is released. If it is preparing, you will cancel AsyncTask and in AsyncTask onCancelled, you will reset and release the object:

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

And this is my releaseync AsyncTask (which just dumps and frees the object):

 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; } } 
0
source

Source: https://habr.com/ru/post/903801/


All Articles