2011-12-14 231 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; 
    } 

}