Android MediaPlayer застрял в режиме подготовки () - PullRequest
6 голосов
/ 15 декабря 2011

Я столкнулся с серьезной проблемой, когда Media Player (MP) зависает при методе prepare(). Мое приложение запускается prepare() в AsyncTask, чтобы избежать блокировки пользовательского интерфейса, так как источники из Интернета. Есть несколько кнопок воспроизведения, которые пользователь может нажимать в любое время, поэтому я добавил prepare() в синхронизированный метод, чтобы лучше контролировать состояние MP. Мое приложение также вызывает release() onPause для освобождения используемых ресурсов.

Дело в том, что я заметил, что если во время подготовки вызывается release(), prepare() никогда не возвращается, и поэтому я застрял внутри синхронизированного метода. Хуже всего то, что поток AsyncTask находится в тупике, и каждый раз, когда пользователь нажимает на воспроизведение в этом состоянии, другой поток теряется, так как он продолжает ждать, чтобы получить монитор, находящийся в распоряжении никогда не возвращающегося prepare(). Вскоре все мои AsyncTasks потоки теряются, и, поскольку я широко их использую, мое приложение перестает работать.

Так что мой вопрос: есть ли у кого-нибудь представление о том, как преодолеть эту проблему? Я серьезно думаю о том, чтобы переделать всю свою работу с MediaPlayer, но мне нужно заранее знать, как лучше справляться с подобными ситуациями.

Ответы [ 4 ]

3 голосов
/ 15 декабря 2011

Вы должны использовать вместо prepareAsync(). И вам не понадобится AsyncTask только для подготовки MediaPlayer.

1 голос
/ 15 декабря 2011

Проблема с использованием Asyntasks или prepareAsync() заключается в том, что он автоматически не переключает состояние MediaPlayer's в подготовленное.Вы могли бы подумать, что это так, но по какой-то причине это не так.Я застрял с этой же проблемой в течение нескольких дней, пока кто-то не предложил мне реализовать OnPreparedListener и использовать его для использования моего MediaPlayer.

Добавить его довольно просто.

Во-первых, внедрите слушателя в своем классе: (я использую это для службы, так что ваш будет выглядеть немного иначе.)

public class MusicService extends Service implements OnPreparedListener

Далее, в вашем утверждении play / prepare используйте 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 голосов
/ 05 мая 2013

Я решил эту проблему в своем приложении следующим образом:

Создание объектов для 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;
    }

}
0 голосов
/ 15 декабря 2011

Спасибо за все ответы, но я исправил это, не используя prepareAsync (), как упоминалось в предыдущих ответах.Я думаю, что если синхронный метод подготовки стал доступен, должен быть способ заставить его работать правильно.

Исправление, хотя и не элегантное IMO, простое: избегайте вызова release () внутри prepare ().Хотя на диаграмме состояний в документах Media Player говорится, что release () можно вызывать в любом состоянии, эксперименты доказали, что вызов этого метода в prepare () (предположительно в состоянии «Подготовка») приведет к зависанию вашего приложения.Поэтому я добавил проверку, чтобы увидеть, находится ли MP в этом состоянии, и теперь я вызываю функцию release (), если это так.Мне нужно было добавить логическое значение в мой код, чтобы проверить это, так как в MP нет метода isPreparing ().

Я уверен, что этого не должно происходить, и это ошибка в самом Android.Как видно из комментариев, в документации есть противоречие: в нем говорится, что release () можно вызывать в любом состоянии, но также говорится, что вызов любых команд, изменяющих состояние, в то время как в состоянии Preparing имеет неопределенный результат.Надеюсь, что это поможет другим людям.

...