Android MediaPlayer Service无法播放Assets目录音频求助
解决Android MediaPlayer Service播放Assets音频失败的问题
我来帮你排查下这个音频播放的问题,你的代码里有几个关键的地方需要调整,咱们一步步来看:
问题分析
- Asset数据源设置错误:直接使用
afd.getFileDescriptor()会指向整个Assets目录的文件描述符,而不是你要播放的单个音频文件,系统无法正确定位到音频数据的位置。 - 同步prepare与监听冲突:你调用了同步的
player.prepare(),之后再设置OnPreparedListener完全无效——因为同步prepare已经完成了,监听根本不会被触发。 - 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
相关产品推荐
相关产品推荐

