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

Android 10及以上MediaSessionService音频无法播放问题排查

Android 10下MediaSessionService播放音频失败排查方案

问题背景

我在安卓音频播放器项目中使用MediaSessionService作为前台服务播放音频。此前使用ExoPlayer搭配普通Service时,通过ContentResolver的getAudios(Activity activity)方法获取设备音频文件并封装进AudioModel对象,在Android 10上运行完全正常。但切换到MediaSessionService后,App在Android 8上可正常运行,Android 10中却无法播放音频。查看日志发现,播放器在缓冲后进入idle状态,推测是MediaItem存在异常。未使用MediaSession、MediaController等组件时,App在Android 8和10上均能正常工作,想知道Android 10是否有额外的权限要求?


一、权限相关排查

Android 10(API 29)引入了Scoped Storage机制,虽然你已经在Manifest中声明了对应权限,但仍需注意以下点:

  • 你的权限请求逻辑中,Android 10属于API < 33的范围,请求的是READ_EXTERNAL_STORAGE,这本身符合规则,但需要确认权限是否被用户正确授予。
  • MediaSessionService作为后台服务,只要App前台已获得存储权限,后台即可正常访问媒体文件,Android 10没有额外的专属权限要求,但需确保权限状态在服务启动时已生效。

二、代码中的核心问题及修复方案

1. MediaSessionCallback的onAddMediaItems方法无效

你的onAddMediaItems方法中,使用peek操作MediaItem的构建器,但未替换原对象,导致修改后的MediaItem未被实际返回,MediaSession无法获取正确的播放源:

// 错误代码
@NonNull
@Override
public ListenableFuture<List<MediaItem>> onAddMediaItems(@NonNull MediaSession mediaSession, @NonNull MediaSession.ControllerInfo controller, @NonNull List<MediaItem> mediaItems) {
    Constants.LOG.mediaSessionLog("MediaSession > onAddMediaItems, size -> "+mediaItems.size());

    List<MediaItem> updatedMediaItems = mediaItems.stream().peek(
            mediaItem ->
            mediaItem.buildUpon()
            .setUri(mediaItem.requestMetadata.mediaUri)
            .build()).collect(Collectors.toList());

    return Futures.immediateFuture(updatedMediaItems);
}

修复方案:使用map替代peek,生成新的MediaItem对象并返回:

// 修复后代码
@NonNull
@Override
public ListenableFuture<List<MediaItem>> onAddMediaItems(@NonNull MediaSession mediaSession, @NonNull MediaSession.ControllerInfo controller, @NonNull List<MediaItem> mediaItems) {
    Constants.LOG.mediaSessionLog("MediaSession > onAddMediaItems, size -> "+mediaItems.size());

    List<MediaItem> updatedMediaItems = mediaItems.stream()
            .map(mediaItem -> mediaItem.buildUpon()
                    .setUri(mediaItem.requestMetadata.mediaUri)
                    .build())
            .collect(Collectors.toList());

    return Futures.immediateFuture(updatedMediaItems);
}

2. AudioPlaybackService中Player实例冲突

在onStartCommand方法中,你重新调用PlayerCreator.getPlayer(getApplicationContext())替换了onCreate中初始化的Player实例,导致MediaSession关联的Player与实际操作的Player不一致,状态同步失败:

// 错误代码:重复创建Player,破坏MediaSession关联
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
    if (intent == null || intent.getAction() == null) return Service.START_NOT_STICKY;

    String action = intent.getAction();

    this.player = PlayerCreator.getPlayer(getApplicationContext()); // 这行代码导致实例冲突
    // ... 后续逻辑
}

修复方案:移除该行代码,直接使用onCreate中初始化的Player实例,或确保PlayerCreator.getPlayer()返回单例对象。

3. Android 10下媒体URI格式错误

在Android 10的Scoped Storage规则下,MediaStore.Audio.Media.DATA字段返回的文件路径无法直接通过Uri.parse()访问,需使用MediaStore的内容URI:

// 错误代码:使用DATA字段生成Uri
while (cursor.moveToNext()) {
    AudioModel model = new AudioModel(Uri.parse(cursor.getString(dataCol)), cursor.getString(nameCol));
    // ... 其他赋值操作
}

修复方案:通过媒体ID生成符合规则的内容URI:

// 修复后代码
while (cursor.moveToNext()) {
    long audioId = cursor.getLong(idCol);
    Uri audioUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, audioId);
    AudioModel model = new AudioModel(audioUri, cursor.getString(nameCol));
    // ... 其他赋值操作
}

生成的URI格式为content://media/external/audio/media/xxx,ExoPlayer可通过ContentResolver正常读取媒体文件。

4. MediaController播放触发逻辑缺失

在setUpPlayer方法中,你仅调用了prepare()但未触发播放,需确保prepare完成后调用play():

// 修复后代码
this.mediaController.setMediaItems(mediaItemList);
this.mediaController.prepare();
this.mediaController.play(); // 添加播放触发
this.binding.progressBarAudioFileLoad.setVisibility(View.GONE);

三、总结修复步骤

  1. 修复MediaSessionCallback的onAddMediaItems方法,使用map生成有效MediaItem。
  2. 移除AudioPlaybackService中重复创建Player的代码,保持MediaSession与Player的关联一致性。
  3. 修改getAudios方法,使用ContentUris生成符合Scoped Storage规则的媒体URI。
  4. 确认Android 10设备上READ_EXTERNAL_STORAGE权限已被正确授予。
  5. 在setUpPlayer中添加播放触发逻辑,确保prepare后调用play()。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 09:57:00