You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 04:45:00