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

Android应用播放raw目录5首音乐的最优方案及异常解决

Android Raw资源音乐播放:MediaPlayer vs 默认播放器,问题排查与最优方案

首先咱们先明确最优方案:对于你这种需要在App内用按钮分别控制5首raw资源音乐播放的场景,直接使用MediaPlayer绝对是最优选择,而不是调用系统默认播放器。

为什么这么说?

  • 调用系统默认播放器会跳转到第三方音乐应用,你的App完全失去对播放流程的控制,用户体验会被打断;
  • MediaPlayer允许你在当前Activity内完全掌控播放、暂停、切换的状态,完美匹配你要的按钮控制逻辑,还能自定义播放交互。

你的代码问题排查

初始代码崩溃原因

  1. 缺少按钮点击监听绑定:你在onCreate里只通过findViewById获取了按钮实例,但没有调用aBTN.setOnClickListener(this);这类代码。如果你的布局里给按钮设置了android:onClick属性,系统找不到对应的处理方法,就会抛出IllegalStateException: Could not execute method for android:onClick。
  2. 变量命名混乱:比如MediaPlayer e对应R.raw.f,Button fBTN绑定的是R.id.e,这种混乱很容易导致后续空指针异常。
  3. 未判空就调用状态方法:直接调用b.isPlaying()这类方法,如果MediaPlayer初始化失败(比如资源不存在),会触发NullPointerException。

修改后代码的问题

  1. MediaPlayer状态错误操作:刚通过MediaPlayer.create创建实例就调用stop(),此时MediaPlayer处于prepared状态,只有started状态才能调用stop(),会触发IllegalStateException。
  2. 重复释放资源:onStop方法里重复调用currentMedia.stop()和release(),第一次release()后currentMedia已经为null,第二次调用会触发NullPointerException。
  3. 播放逻辑不完善:没有正确处理“同一首歌暂停后继续播放”的场景,逻辑分支存在错误。

修正后的完整代码

下面是优化后的代码,解决了上述所有问题,并且复用单个MediaPlayer节省内存:

public class MusicActivity extends AppCompatActivity implements View.OnClickListener {
    private MediaPlayer currentMediaPlayer;
    private Button btnA, btnB, btnC, btnD, btnE;
    // 记录当前正在播放的资源ID,0表示未播放
    private int currentPlayingResId = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_music);

        // 绑定按钮并设置点击监听
        btnA = findViewById(R.id.a);
        btnB = findViewById(R.id.b);
        btnC = findViewById(R.id.c);
        btnD = findViewById(R.id.d);
        btnE = findViewById(R.id.e);

        btnA.setOnClickListener(this);
        btnB.setOnClickListener(this);
        btnC.setOnClickListener(this);
        btnD.setOnClickListener(this);
        btnE.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        int targetResId = 0;
        switch (view.getId()) {
            case R.id.a:
                targetResId = R.raw.a;
                break;
            case R.id.b:
                targetResId = R.raw.b;
                break;
            case R.id.c:
                targetResId = R.raw.c;
                break;
            case R.id.d:
                targetResId = R.raw.d;
                break;
            case R.id.e:
                targetResId = R.raw.f; // 这里注意你的资源对应关系,根据实际情况调整
                break;
        }

        if (targetResId != 0) {
            handleMusicPlay(targetResId);
        }
    }

    private void handleMusicPlay(int targetResId) {
        // 点击的是当前正在播放的音乐
        if (currentPlayingResId == targetResId) {
            if (currentMediaPlayer != null && currentMediaPlayer.isPlaying()) {
                currentMediaPlayer.pause();
            } else if (currentMediaPlayer != null) {
                currentMediaPlayer.start();
            }
            return;
        }

        // 切换到新的音乐,先释放之前的资源
        releaseCurrentMediaPlayer();

        // 创建新的MediaPlayer并播放
        currentMediaPlayer = MediaPlayer.create(this, targetResId);
        if (currentMediaPlayer != null) {
            currentMediaPlayer.start();
            currentPlayingResId = targetResId;
            // 可选:设置播放完成监听,播放完后重置状态
            currentMediaPlayer.setOnCompletionListener(mp -> {
                releaseCurrentMediaPlayer();
                currentPlayingResId = 0;
            });
        }
    }

    // 统一释放MediaPlayer资源
    private void releaseCurrentMediaPlayer() {
        if (currentMediaPlayer != null) {
            if (currentMediaPlayer.isPlaying()) {
                currentMediaPlayer.stop();
            }
            currentMediaPlayer.release();
            currentMediaPlayer = null;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 页面停止时释放资源
        releaseCurrentMediaPlayer();
        currentPlayingResId = 0;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseCurrentMediaPlayer();
    }
}

代码优化点说明

  • 复用单个MediaPlayer:避免创建多个实例占用内存,统一管理播放状态。
  • 完善的状态判断:所有操作前都判空,避免空指针;严格遵循MediaPlayer的状态流程。
  • 统一资源释放:封装releaseCurrentMediaPlayer方法,在页面停止、销毁、切换音乐时都调用,避免内存泄漏。
  • 清晰的变量命名:避免之前的命名混乱问题,代码可读性更强。
  • 播放完成监听:可选添加,播放完成后自动重置状态,避免无效引用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 10:37:45