2011-12-14 12 views
6

prepare()メソッドで停止しているメディアプレーヤー(MP)に重大な問題があります。 私のアプリはAsyncTaskの中にprepare()を実行しています。ソースはウェブからのものなので、UIをブロックすることはありません。ユーザーがいつでもクリックできるいくつかの「再生」ボタンがあるので、同期方法の中にprepare()を追加して、MPの状態をより適切に制御します。私のアプリはまた、release() onPauseを呼び出して、使用済みのリソースを解放します。Android MediaPlayerが準備完了(

準備中にrelease()が呼び出された場合、prepare()が返ってこないので、同期メソッド内に詰まっていることに気付きました。最も悪いのは、AsyncTaskスレッドがデッドロック状態にあり、その状態でユーザーが再生をクリックするたびに、決して返されないprepare()が存在するモニターの取得を待機しているので、別のスレッドが無駄になることです。まもなく私のAsyncTasksスレッドは無駄になり、私はそれらを広範囲に使用するので、私のアプリは動作しなくなります。

だから私の質問です:どのようにこの問題を克服するアイデアを持っていますか? MediaPlayerですべての作業をやり直すことを真剣に考えていますが、このような状況を事前に処理するための最良の方法を知る必要があります。

答えて

3

代わりにprepareAsync()を使用してください。 MediaPlayerの準備のためだけに、AsyncTaskは必要ありません。

+0

私はそれについて考えましたが、私はそれが手元の問題を解決することを確信していません。 MediaPlayerドキュメントには、「準備中状態は一時的な状態であり、MediaPlayerオブジェクトが準備中の状態で副作用を伴うメソッドを呼び出す動作は定義されていないことに注意することが重要です。だから、問題は残っています。私は '準備中'の状態でrelease()を呼び出し、その結果は未定義です。私は同期準備()の中で、MediaPlayerがこの「準備中」状態に移行し、それが私が受け取っているエラーを引き起こすと思われます。 – hgm

+0

図と状態テーブルによれば、どの状態からでも 'release()'を呼び出すことができます。試してごらん。私はマルチスレッド関連のバグに直面していると思います。複数のスレッドを除外すると、それを "修正"する必要があります。 – inazaruk

+0

私はrelease()をいつでも呼び出すことができると言っていますが、これをprepare()の中で呼び出すと、私は止まってしまうという問題に直面しています。これは起こるべきではなく、Android IMOのバグです。 – hgm

0

AsyntasksまたはprepareAsync()を使用する際の問題は、自動的に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()を呼び出すことは避けてください。 Media Playerのドキュメントの状態図では、release()はどの状態でも呼び出すことができますが、prepare()(おそらく 'Preparing'状態)で呼び出すと、アプリケーションがハングすることが実験によって証明されました。そこで、MPがこの状態にあるかどうかを確認するためのチェックを追加しました。もしあれば、release()を呼び出すようになりました。これをチェックするには、MPにisPreparing()メソッドがないので、私は自分のコードにbooleanを追加する必要がありました。

これは起こるべきではなく、Android自体のバグです。コメントに見られるように、ドキュメントには矛盾があります:release()はどの状態でも呼び出すことができますが、Preparing状態の間に状態を変更するコマンドを呼び出すと結果は不定になります。これが他の人々に役立つことを願っています。

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

} 
関連する問題