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

Android MediaPlayer Service无法播放Assets目录音频求助

解决Android MediaPlayer Service播放Assets音频失败的问题

我来帮你排查下这个音频播放的问题,你的代码里有几个关键的地方需要调整,咱们一步步来看:

问题分析

  1. Asset数据源设置错误:直接使用afd.getFileDescriptor()会指向整个Assets目录的文件描述符,而不是你要播放的单个音频文件,系统无法正确定位到音频数据的位置。
  2. 同步prepare与监听冲突:你调用了同步的player.prepare(),之后再设置OnPreparedListener完全无效——因为同步prepare已经完成了,监听根本不会被触发。
  3. MediaPlayer生命周期管理疏漏:每次启动Service都会新建MediaPlayer实例,但没有处理之前的实例(如果存在的话),容易造成资源泄漏;而且播放完成后释放了player,但成员变量没有置空,下次启动可能出现空指针或资源占用问题。

修正后的Service代码

public class MinesweeperSound extends Service {
    private MediaPlayer player; // 用private修饰,规范成员变量访问

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 先释放之前的MediaPlayer实例,避免资源泄漏
        if (player != null) {
            player.release();
            player = null;
        }

        try {
            String audioFile = intent.getStringExtra("filename");
            if (audioFile == null) {
                throw new IllegalArgumentException("音频文件名不能为空");
            }

            AssetFileDescriptor afd = getAssets().openFd(audioFile);
            player = new MediaPlayer();
            // 关键:使用带offset和length的setDataSource重载方法
            player.setDataSource(
                afd.getFileDescriptor(),
                afd.getStartOffset(),
                afd.getLength()
            );
            afd.close(); // 用完AssetFileDescriptor记得关闭

            // 使用异步prepare配合监听,避免阻塞主线程
            player.prepareAsync();
            player.setOnPreparedListener(mediaPlayer -> mediaPlayer.start());
            
            player.setOnCompletionListener(mediaPlayer -> {
                mediaPlayer.release();
                player = null; // 释放后置空成员变量
                stopSelf(); // 播放完成后停止Service,避免不必要的后台占用
            });

            player.setOnErrorListener((mediaPlayer, what, extra) -> {
                mediaPlayer.release();
                player = null;
                stopSelf();
                Log.e("MinesweeperSound", "音频播放错误: what=" + what + ", extra=" + extra);
                return true;
            });

        } catch (IOException | IllegalArgumentException e) {
            e.printStackTrace();
            Log.e("MinesweeperSound", "初始化音频失败: " + e.getMessage());
            // 初始化失败时停止Service
            stopSelf();
        }
        return Service.START_NOT_STICKY; // 播放完成后不需要重启Service,用START_NOT_STICKY更合适
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // Service销毁时确保释放MediaPlayer资源
        if (player != null) {
            player.release();
            player = null;
        }
    }
}

关键修改说明

  • 正确设置Asset数据源:通过afd.getStartOffset()和afd.getLength()告诉MediaPlayer音频数据在文件描述符中的具体位置,这是播放Assets音频的核心要点。
  • 异步prepare+监听:使用prepareAsync()避免阻塞Service的主线程,同时通过OnPreparedListener在准备完成后启动播放。
  • 完善资源管理:每次启动前释放旧的MediaPlayer实例,播放完成或出错后及时释放资源并停止Service,避免后台占用和泄漏。
  • 错误监听:添加OnErrorListener捕获播放过程中的错误,方便排查问题。

额外注意事项

  • 确保correct.mp3和incorrect.mp3确实放在src/main/assets目录下,文件名大小写要和代码中的一致(Android Assets目录区分大小写)。
  • 如果你的应用目标SDK是Android 8.0(API 26)及以上,后台Service的启动会受到限制,这种短时间播放音频的场景,建议改用startForegroundService并在5秒内调用startForeground显示一个低优先级通知,避免Service被系统杀死。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:34:54