更改播放器的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
方法。目前尚不清楚球员的表现如何......
还有其他好的解决方案吗?
答案 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比其他版本更糟糕,有时它只是做了奇怪的事情。你应该有一个能够对这些情况作出反应的接口/监听器
<强>更新强>:
这是我停止(或暂停)我的音乐播放时使用的方法(主要来自示例应用,这是在服务中运行,并且已经过修改以适合我自己的应用但仍然存在)。
第一种方法由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();
}
}
作为最后一点,我注意到,当音频流或源不可用或不可靠时,会发生“阻止所有内容的媒体播放器”。
祝你好运!如果您有任何具体要求,请告诉我。答案 1 :(得分:0)
最新的手机和Android API工作很多,reset
方法在歌曲之间快速切换(下一个或上一个)时只需5-20毫秒
因此没有旧手机的解决方案,它只是如何运作