tôi giải quyết vấn đề này trong ứng dụng của tôi như thế này:
Tạo đối tượng cho AsyncTasks (do đó bạn có thể kiểm tra nếu họ đang trong tiến trình):
private AsyncTask<String, Void, String> releaseMP;
private AsyncTask<String, Void, String> setSource;
Tạo một AsyncTask cho chuẩn bị cuộc gọi :
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() {
}
}
Bây giờ chuẩn bị của bạn mã:
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);
Và cuối cùng, khi bạn cần tiêu diệt mediaPlayer, bạn sẽ kiểm tra đối tượng setSource để xem nó có đang chuẩn bị trước khi bạn phát hành nó hay không. Nếu nó được chuẩn bị, bạn sẽ hủy AsyncTask và trong AsyncTask onCancelled, bạn sẽ thiết lập lại và thả đối tượng:
public void release(final boolean cleartargetstate) {
if (mMediaPlayer != null) {
if (setSource != null) {
setSource.cancel(true);
} else {
releaseMP = new releaseMP().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
}
}
}
Và đây là releaseMP AsyncTask của tôi (mà chỉ reset và giải phóng đối tượng):
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;
}
}
Tôi đã nghĩ về điều đó, nhưng tôi không bỏ chắc chắn rằng nó sẽ giải quyết được sự cố trong tầm tay. Tài liệu MediaPlayer nói rằng "Điều quan trọng cần lưu ý là trạng thái Chuẩn bị là trạng thái tạm thời và hành vi gọi bất kỳ phương thức nào có hiệu ứng phụ trong khi đối tượng MediaPlayer đang ở trạng thái Đang chuẩn bị chưa được xác định." Vì vậy, vấn đề sẽ vẫn còn, tôi sẽ gọi release() trong trạng thái 'đang chuẩn bị', và kết quả là không xác định. Tôi nghi ngờ rằng bên trong chuẩn bị đồng bộ của tôi(), MediaPlayer chuyển sang trạng thái "đang chuẩn bị" này và kết quả là trong các erros mà tôi nhận được. – hgm
Theo sơ đồ và bảng trạng thái, bạn có thể gọi 'release()' từ bất kỳ trạng thái nào. Thôi buông đi. Tôi nghĩ rằng bạn đang phải đối mặt với lỗi đa luồng liên quan. Và loại trừ nhiều chủ đề nên "sửa" nó. – inazaruk
Nó nói rằng tôi có thể gọi phát hành() bất cứ lúc nào, nhưng ngay cả như vậy tôi đang phải đối mặt với vấn đề bị mắc kẹt nếu tôi gọi đây bên trong một chuẩn bị(). Điều này không nên xảy ra và là một lỗi trong Android IMO. – hgm