Android MediaPlayer重置冻结UI

时间:2014-02-07 15:45:29

标签: java android android-mediaplayer android-ui

更改播放器的MediaPlayer时,Android dataSource出现问题。根据{{​​1}}(http://developer.android.com/reference/android/media/MediaPlayer.html)的规范,我必须MediaPlayer更改reset时的播放器。这很好用,但只要快速连续调用dataSource方法两次,channelChanged就会冻结用户界面。我分析了这里看到的代码:

MediaPlayer.reset
  

重置:3

     

setDataSource:1

     

准备:0

     

重置:3119

     

setDataSource:2

     

准备:1

显然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)); } 被第一次调用的reset阻止(当我等到第一个流开始然后再次调用asynchronous preparing时,一切都很好)。

任何想法如何解决问题?我应该在一个单独的线程中执行整个方法吗?基本上我想避免这种情况,因为它似乎不是一种好的编码风格,并且可能会引起一些进一步的问题,例如:当用户再次尝试启动播放器时,播放器仍处于channelChanged()方法中,而另一方面,该方法似乎等待reset方法。目前尚不清楚球员的表现如何......

还有其他好的解决方案吗?

2 个答案:

答案 0 :(得分:12)

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比其他版本更糟糕,有时它只是做了奇怪的事情。你应该有一个能够对这些情况作出反应的接口/监听器

<强>更新

这是我停止(或暂停)我的音乐播放时使用的方法(主要来自示例应用,这是在服务中运行,并且已经过修改以适合我自己的应用但仍然存在)。

第一种方法由stoppause使用,前者通过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();
    }
}

作为最后一点,我注意到,当音频流或源不可用或不可靠时,会发生“阻止所有内容的媒体播放器”。

祝你好运!如果您有任何具体要求,请告诉我。

答案 1 :(得分:0)

最新的手机和Android API工作很多,reset方法在歌曲之间快速切换(下一个或上一个)时只需5-20毫秒

因此没有旧手机的解决方案,它只是如何运作