Ho risolto questo problema nella mia app come questo:
creare oggetti per i AsyncTasks (in modo da poter verificare se sono in corso):
private AsyncTask<String, Void, String> releaseMP;
private AsyncTask<String, Void, String> setSource;
Crea un AsyncTask per la chiamata preparare :
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() {
}
}
Ora il vostro codice di preparare:
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);
Infine, quando occorre uccidere MediaPlayer, si controlla l'oggetto setSource per verificare se si sta preparando prima di rilasciarlo. Se si sta preparando, si annulla l'AsyncTask e nel AsyncTask onCancelled, si resetta e rilasciare l'oggetto:
public void release(final boolean cleartargetstate) {
if (mMediaPlayer != null) {
if (setSource != null) {
setSource.cancel(true);
} else {
releaseMP = new releaseMP().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
}
}
}
E questa è la mia releaseMP AsyncTask (che ha appena ripristina e rilascia l'oggetto):
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;
}
}
Ci ho pensato, ma non ho smesso di sicuro che avrebbe risolto il problema. I documenti di MediaPlayer dicono che "È importante notare che lo stato di preparazione è uno stato transitorio e il comportamento di chiamare qualsiasi metodo con effetto collaterale mentre un oggetto MediaPlayer è in stato di preparazione non è definito." Quindi il problema rimarrebbe, chiamerei release() nello stato di "preparazione" e il risultato non è definito. Sospetto che all'interno della mia preparazione sincrona(), MediaPlayer passi a questo stato di "preparazione" e questo comporti gli errori che sto ricevendo. – hgm
Secondo il diagramma e la tabella di stato puoi chiamare 'release()' da qualsiasi stato. Provaci. Penso che tu stia affrontando un bug relativo al multithreading. E l'esclusione di più thread dovrebbe "aggiustarlo". – inazaruk
Si dice che posso chiamare release() in qualsiasi momento, ma anche così sto affrontando il problema di rimanere bloccato se lo chiamo all'interno di un prepare(). Questo non dovrebbe accadere ed è un bug in IMO Android. – hgm