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

Android MediaPlayer使用setOnCompletionListener自动播放下一曲问题求助

解决MediaPlayer自动播放下一曲的NullPointerException问题

先帮你定位下这个java.lang.NullPointerException: Attempt to read from null array的核心原因:

  • 在onCompletion方法里,你直接把position + 1转成字符串当音频URL用,这根本不是有效的资源地址啊!比如当前播放position0,你传的是"1",而不是对应的https://embeye.tech/ummi/speaker/jilidsatu/0101.mp3,MediaPlayer处理无效URL时就会抛出空指针异常。
  • 另外,你依赖的是OnItemClick里的final局部position变量,这个值只会固定你点击时的歌曲索引,没法动态更新当前播放的位置,后续自动播放多首后逻辑会彻底混乱。

下面给你一套完整的修复方案,还顺便帮你简化了冗余代码:

1. 统一管理歌曲资源

先把所有歌曲的URL和标题整理成数组,告别一堆冗余的if-else,也方便后续自动播放时快速获取资源:

// 全局定义歌曲数组,存储每首歌的URL和标题
private String[][] songList = {
    {"https://embeye.tech/ummi/speaker/0001.mp3", "Doa Pembuka"},
    {"https://embeye.tech/ummi/speaker/jilidsatu/0101.mp3", "Halaman Satu"},
    {"https://embeye.tech/ummi/speaker/jilidsatu/0102.mp3", "Halaman Dua"},
    {"https://embeye.tech/ummi/speaker/jilidsatu/0103.mp3", "Halaman Tiga"}
};
private int currentSongPosition = -1; // 记录当前播放的歌曲索引
private MediaPlayer sound;

2. 重构ListView点击事件

简化原来的逻辑,同时用全局变量跟踪当前播放位置:

list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        mButtonPause.setEnabled(true);
        mButtonResume.setEnabled(false);
        // 更新当前播放的歌曲索引
        currentSongPosition = position;
        // 获取当前歌曲的URL和标题
        String audioUrl = songList[currentSongPosition][0];
        String songTitle = songList[currentSongPosition][1];
        
        try {
            // 重置或初始化MediaPlayer
            if (sound != null) {
                sound.reset();
            } else {
                sound = new MediaPlayer();
            }
            sound.setAudioStreamType(AudioManager.STREAM_MUSIC);
            sound.setDataSource(audioUrl);
            sound.prepareAsync();
            
            // 准备完成后开始播放
            sound.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    mp.start();
                    halaman.setText(songTitle);
                    Toast.makeText(mContext,"Playing",Toast.LENGTH_SHORT).show();
                    initializeSeekBar();
                    // 更新按钮可用状态
                    mButtonNext.setEnabled(currentSongPosition < songList.length - 1);
                    mButtonPrev.setEnabled(currentSongPosition > 0);
                }
            });
            
            // 设置播放完成监听(放到这里避免重复绑定)
            sound.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mediaPlayer) {
                    Toast.makeText(mContext,"End",Toast.LENGTH_SHORT).show();
                    mButtonResume.setEnabled(true);
                    // 自动触发下一曲播放
                    playNextSong();
                }
            });
            
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(mContext,"播放失败",Toast.LENGTH_SHORT).show();
        }
    }
});

3. 封装自动播放下一曲的逻辑

单独写一个方法处理下一曲播放,还能处理最后一首播放完的边界情况:

private void playNextSong() {
    // 判断是否还有下一曲
    if (currentSongPosition < songList.length - 1) {
        currentSongPosition++;
        String audioUrl = songList[currentSongPosition][0];
        String songTitle = songList[currentSongPosition][1];
        
        try {
            sound.reset();
            sound.setAudioStreamType(AudioManager.STREAM_MUSIC);
            sound.setDataSource(audioUrl);
            sound.prepareAsync();
            sound.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    mp.start();
                    halaman.setText(songTitle);
                    Toast.makeText(mContext,"自动播放下一曲",Toast.LENGTH_SHORT).show();
                    initializeSeekBar();
                    // 更新按钮状态
                    mButtonNext.setEnabled(currentSongPosition < songList.length - 1);
                    mButtonPrev.setEnabled(currentSongPosition > 0);
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(mContext,"下一曲播放失败",Toast.LENGTH_SHORT).show();
        }
    } else {
        // 已经是最后一首,提示并禁用下一曲按钮
        Toast.makeText(mContext,"所有歌曲播放完毕",Toast.LENGTH_SHORT).show();
        mButtonNext.setEnabled(false);
    }
}

核心修复点说明

  • 用数组统一管理歌曲资源,既避免了冗余代码,又能确保自动播放时获取到正确的音频URL;
  • 用全局的currentSongPosition跟踪播放位置,替代原来的局部position变量,逻辑更准确;
  • 封装playNextSong方法,让自动播放的逻辑更清晰,也方便后续维护;
  • 每次播放前重置MediaPlayer,避免状态异常,同时处理了播放完毕的边界情况。

内容的提问来源于stack exchange,提问作者masbayu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:06:55