Android开发:MediaPlayer调用reset后setDataSource仍报IllegalStateException求助
问题根因
- 多线程操作同一个MediaPlayer实例导致状态混乱:你在
onChanged方法中启动子线程执行reset、setDataSource等操作,若快速点击歌曲列表,会同时生成多个子线程并发修改MediaPlayer状态,状态机不符合调用规范就会抛出该异常。 - 状态调用顺序错误:你在主线程判断
mediaPlayer.isPlaying()后执行pause、stop,但stop方法只能在Started、Paused等特定状态调用,若MediaPlayer处于Idle、Initialized等状态调用stop会直接抛出异常,后续子线程的reset还没执行流程就已经崩溃。 - 逻辑递归冲突:
onPlay、onNext、onPrev方法中先操作MediaPlayer,末尾又调用onChanged,onChanged内又会重新停止、加载音频,逻辑重复冲突进一步打乱MediaPlayer状态。 - 监听器与Timer重复设置:每次
onChanged都会新建Timer且没有销毁旧Timer,同时你在Timer的定时任务中每秒重复设置OnCompletionListener,多余的回调也会干扰MediaPlayer状态。
修复方案
1. 加防重标记避免频繁触发切换
在Activity中新增全局变量:
private boolean isSwitchingSong = false;
在onChanged方法开头先判断状态,正在切换就直接返回:
@Override public void onChanged(int position) { if (isSwitchingSong) return; isSwitchingSong = true; // 后续原有逻辑 }
在音频加载完成或者抛出异常后把isSwitchingSong改回false。
2. 统一MediaPlayer操作的线程和状态处理
把停止、重置操作放到子线程开头,不要分散在主线程和子线程,同时移除不必要的stop调用:
// 移除原主线程的if(mediaPlayer.isPlaying()){pause、stop}逻辑 new Thread(new Runnable() { @Override public void run() { try { // 先重置MediaPlayer,不管当前是什么状态直接回到Idle mediaPlayer.reset(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(MainActivity.this, musicLists.get(currentSongNumber).getMusicFile()); MainActivity.this.runOnUiThread(() -> { musicbar_name.setText(musicLists.get(currentSongNumber).getTitle()); }); mediaPlayer.prepare(); mediaPlayer.start(); isSwitchingSong = false; } catch (IOException e) { isSwitchingSong = false; new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(MainActivity.this, "无法播放该曲目!", Toast.LENGTH_SHORT).show()); e.printStackTrace(); } catch (Exception e) { isSwitchingSong = false; e.printStackTrace(); } } }).start();
3. 移除递归调用逻辑
删除onPlay、onPrev、onNext方法中直接操作mediaPlayer.start()的代码,所有播放逻辑统一由onChanged处理,比如onPlay修改为:
@Override public void onPlay() { CreateNotification.createNotification(MainActivity.this,musicLists.get(currentSongNumber),R.drawable.ic_baseline_pause_24,currentSongNumber,musicLists.size()-1); isPlaying = true; playpauseImage.setImageResource(R.drawable.pause_svg); musicLists.get(currentSongNumber).setPlaying(true); musicbar_name.setText(musicLists.get(currentSongNumber).getTitle()); musicAdapater.update(musicLists); musicRecyclerV.scrollToPosition(currentSongNumber); onChanged(currentSongNumber); }
4. 优化Timer和监听器设置
每次进入onChanged先销毁旧Timer,同时把OnCompletionListener、OnPreparedListener的设置移到子线程中,不要放在Timer定时任务里:
@Override public void onChanged(int position) { // 开头先销毁旧Timer if (timer != null) { timer.cancel(); timer.purge(); timer = null; } currentSongNumber = position; CreateNotification.createNotification(MainActivity.this,musicLists.get(currentSongNumber),R.drawable.ic_baseline_pause_24,currentSongNumber,musicLists.size()-1); // 子线程逻辑 new Thread(new Runnable() { @Override public void run() { try { mediaPlayer.reset(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(MainActivity.this, musicLists.get(currentSongNumber).getMusicFile()); // 直接在这里设置监听器 mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { final int getTotalDuration = mp.getDuration(); String generateDuration = String.format(Locale.getDefault(),"%02d:%02d", TimeUnit.MILLISECONDS.toMinutes(getTotalDuration), TimeUnit.MILLISECONDS.toSeconds(getTotalDuration)- TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(getTotalDuration))); endTime.setText(generateDuration); isPlaying = true; mp.start(); playSeekbar.setMax(getTotalDuration); playpauseImage.setImageResource(R.drawable.pause_svg); } }); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mediaPlayer.reset(); String tm = "00:00"; startTime.setText(tm); if (timer != null) { timer.purge(); timer.cancel(); timer = null; } isPlaying = false; playpauseImage.setImageResource(R.drawable.ic_baseline_play_arrow_24); playSeekbar.setProgress(0); musicAdapater.update(musicLists); musicRecyclerV.scrollToPosition(0); // 播放下一首音乐 onNext(); } }); mediaPlayer.prepare(); } catch (Exception e) { // 异常处理逻辑 } } }).start(); // 启动Timer刷新进度 timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { if (mediaPlayer != null && isPlaying) { final int getCurrentDuration = mediaPlayer.getCurrentPosition(); String generateDuration = String.format(Locale.getDefault(),"%02d:%02d", TimeUnit.MILLISECONDS.toMinutes(getCurrentDuration), TimeUnit.MILLISECONDS.toSeconds(getCurrentDuration)- TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(getCurrentDuration))); playSeekbar.setProgress(getCurrentDuration); startTime.setText(generateDuration); } } }); } },1000,1000); }
补充优化建议
可以用mediaPlayer.prepareAsync()代替手动开线程调用prepare(),系统原生提供的异步准备API更稳定,不需要自己维护子线程。
内容的提问来源于stack exchange,提问作者Gourav Modi
相关产品推荐
相关产品推荐

