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
相关产品推荐
相关产品推荐

