Android MediaPlayer重置会冻结UI

更改播放器的dataSource时,Android MediaPlayer出现问题。 根据MediaPlayer的规范( http://developer.android.com/reference/android/media/MediaPlayer.html ),我必须在更改dataSourcereset播放器。 这工作正常,但只要连续两次调用channelChanged方法, MediaPlayer.reset冻结UI。 我在这里看到代码的configuration文件:

 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阻塞(当我等到第一个stream开始,然后再次调用channelChanged()时,一切都很好)。

任何想法如何解决问题? 我应该在一个单独的线程中执行整个方法吗? 基本上我想避免这一点,因为它似乎不是一个好的编码风格,并可能导致一些进一步的问题,例如,当用户试图再次启动播放器,但播放器仍在reset方法,而在另一个手似乎在等待asyncPrepare方法。 目前还不清楚玩家会如何performance

有没有其他好的解决scheme?

MediaPlayer是一个棘手的混蛋。 我build议你看一下示例应用程序,其中的MediaPlayer糟糕的devise是显而易见的,通过查看你必须编写的代码混乱,以获得一致的媒体播放体验。

如果有的话,看样品后,你会发现,当他们想要跳过一个轨道,他们基本上重置并释放…

  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 ,前者通过,后者为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示例如何执行(在示例文件夹中find),我没有遇到问题。

这就是说,我知道你说的是什么意思,当你把所有的东西都封锁的时候,我已经看到了,这是MP的麻烦。 如果你得到一个ANR,我想看看它的日志。

对于这里的logging,我是如何“开始玩”的(很多自定义代码已经被省略了,但是你可以看到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(); } } 

作为最后一点,我注意到当audiostream或audio源不可用或不可靠时,“媒体播放器阻塞一切”就会发生。

祝你好运! 让我知道是否有什么具体的你想看到。

最新的手机和Android API工作的黄油, reset方法只需要5-20毫秒快速切换歌曲之间(next或prev)

所以老的手机没有解决scheme,它只是如何工作