Сброс Android MediaPlayer замораживает пользовательский интерфейс

у меня проблема с Android MediaPlayer при изменении dataSource игрока. Согласно спецификации MediaPlayer (http://developer.android.com/reference/android/media/MediaPlayer.html) я reset игрок при смене dataSource. Это работает нормально, но как только channelChanged метод вызывается дважды в быстрой последовательности элементов MediaPlayer.reset замораживает пользовательский интерфейс. Я профилирую код, как видно здесь:

public void channelChanged(String streamingUrl)
{
    long m1 = System.currentTimeMillis();
    mMediaPlayer.reset();
    long m2 = System.currentTimeMillis();
    try
    {
        mMediaPlayer.setDataSource(streamingUrl);
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
    long m3 = System.currentTimeMillis();
    mMediaPlayer.prepareAsync();
    long m4 = System.currentTimeMillis();
    Log.d("MEDIAPLAYER", "reset: " + (m2 - m1));
    Log.d("MEDIAPLAYER", "setDataSource: " + (m3 - m2));
    Log.d("MEDIAPLAYER", "preparing: " + (m4 - m3));
}

сброс: 3

setDataSource: 1

готовим: 0

сброс: 3119

setDataSource: 2

Готовим: 1

так видимо reset заблокирован asynchronous preparing первого вызова (когда я жду, пока не начнется первый поток, а затем вызовите

2 ответов


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

если что-нибудь, посмотрев на образец, вы видите, что, когда они хотят пропустить трек, они по существу сброс и освободить...

    mPlayer.reset();
    mPlayer.release();

...и позже, когда они будут готовы для загрузки новый трек...

    try {
          mPlayer.reset();
          mPlayer.setDataSource(someUrl);
          mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
             @Override
              public void onPrepared(MediaPlayer mediaPlayer) {
                   //bam!
              }
          });
          mPlayer.prepareAsync();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    }

я добавил try/catch, потому что на некоторых устройствах / версиях ОС MediaPlayer хуже, чем другие, и иногда он просто делает странные вещи. У вас должен быть интерфейс / слушатель, который способен реагировать на эти ситуации

обновление:

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

первый метод используется обоими stop и pause бывший передает true, позже false

/**
 * Releases resources used by the service for playback. This includes the "foreground service"
 * status and notification, the wake locks and possibly the MediaPlayer.
 *
 * @param releaseMediaPlayer Indicates whether the Media Player should also be released or not
 */
void relaxResources(boolean releaseMediaPlayer) {
    stopForeground(true);
    stopMonitoringPlaybackProgress();
    // stop and release the Media Player, if it's available
    if (releaseMediaPlayer && mPlayer != null) {
        mPlayer.reset();
        mPlayer.release();
        mPlayer = null;
    }
    // we can also release the Wifi lock, if we're holding it
    if (mWifiLock.isHeld()) {
        mWifiLock.release();
    }
}

это часть processPauseRequest():

if (mState == State.Playing) {
        // Pause media player and cancel the 'foreground service' state.
        mState = State.Paused;
        mPlayer.pause();
        dispatchBroadcastEvent(ServiceConstants.EVENT_AUDIO_PAUSE);//notify broadcast receivers
        relaxResources(false); // while paused, we always retain the mp and notification

и это часть processStopRequest() (упрощенный):

void processStopRequest(boolean force, final boolean stopSelf) {
    if (mState == State.Playing || mState == State.Paused || force) {
        mState = State.Stopped;
        // let go of all resources...
        relaxResources(true);
        currentTrackNotification = null;
        giveUpAudioFocus();         

    }
}

теперь основная часть Следующая / пропустить...

вот что я делаю...

void processNextRequest(final boolean isSkipping) {
    processStopRequest(true, false); // THIS IS IMPORTANT, WE RELEASE THE MP HERE
    mState = State.Retrieving;
    dispatchBroadcastEvent(ServiceConstants.EVENT_TRACK_INFO_LOAD_START);
    // snipped but here you retrieve your next track and when it's ready…
    // you just processPlayRequest() and "start from scratch"

вот как это делает образец MediaPlayer (найден в папке samples), и у меня не было проблема с ним.

это, как говорится, Я знаю, что вы имеете в виду, когда вы говорите, что все это заблокировано, я видел это, и это багги MP. Если вы получите ANR, я хотел бы увидеть журнал для него.

для записи вот как я "начинаю играть" (много пользовательского кода было опущено, но вы можете увидеть материал MP):"

/**
 * Starts playing the next song.
 */
void beginPlaying(Track track) {
    mState = State.Stopped;
    relaxResources(false); // release everything except MediaPlayer
    try {
        if (track != null) {
            createMediaPlayerIfNeeded();
            mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mPlayer.setDataSource(track.audioUrl);
        } else {
            processStopRequest(true, false); // stop everything! 
            return;
        }
        mState = State.Preparing;
        setUpAsForeground(); //service

        /* STRIPPED ALL CODE FROM REMOTECONTROLCLIENT, AS IT ADDS A LOT OF NOISE :) */

        // starts preparing the media player in the background. When it's done, it will call
        // our OnPreparedListener (that is, the onPrepared() method on this class, since we set
        // the listener to 'this').
        // Until the media player is prepared, we *cannot* call start() on it!
        mPlayer.prepareAsync();
        // We are streaming from the internet, we want to hold a Wifi lock, which prevents
        // the Wifi radio from going to sleep while the song is playing.
        if (!mWifiLock.isHeld()) {
            mWifiLock.acquire();
        }

    } catch (IOException ex) {
        Log.e("MusicService", "IOException playing next song: " + ex.getMessage());
        ex.printStackTrace();
    }
}

в качестве заключительного замечания я заметил ,что "медиаплеер блокирует все" происходит, когда аудиопоток или источник недоступна или ненадежна.

удачи! Дай мне знать, если захочешь увидеть что-то конкретное.


новейшие телефоны и Android API работает много масла,reset метод занимает всего 5-20 мс при быстром переключении между песнями (next или prev)

таким образом, нет решения для старых телефонов, это просто, как это работает