Flutter使用AudioService时如何请求远程API更新非播放状态队列项的URL
按需加载音频队列URL的实现方案
你可以通过动态更新ConcatenatingAudioSource子项+暴露自定义更新方法实现需求,不需要一次性预加载全量队列的URL,具体实现如下:
1. 改造背景音频任务的基础属性
首先在你的背景音频任务类中定义全局队列和音频源实例:
// 全局维护音频队列 List<MediaItem> queue = []; // 拼接音频源实例,支持动态修改子项 late ConcatenatingAudioSource concatenatingAudioSource; // 全局播放器实例(你原本应该已经定义) final _player = AudioPlayer();
2. 重写onUpdateQueue方法,移除全量URL加载逻辑
初始化时仅用占位URI构建音频源,不发起批量API请求:
@override Future<void> onUpdateQueue(List<MediaItem> _queue) async { queue = _queue; // 同步更新后台通知队列 await AudioServiceBackground.setQueue(queue); // 初始化拼接音频源,无有效URL的项先占空位 concatenatingAudioSource = ConcatenatingAudioSource( children: queue.map((item) { return AudioSource.uri( Uri.parse(item.extras?['url'] ?? 'about:blank'), tag: item, ); }).toList(), ); await _player.setAudioSource(concatenatingAudioSource); // 首次加载时只请求当前要播放的项的URL await _updateQueueItemUrl(index!); await _player.seek(Duration.zero, index: index); await AudioServiceBackground.setMediaItem(queue[index!]); }
3. 实现单队列项URL更新方法
单独封装API请求、MediaItem更新、音频源替换的逻辑:
/// 更新指定索引队列项的音频URL Future<void> _updateQueueItemUrl(int targetIndex) async { // 索引越界直接返回 if (targetIndex < 0 || targetIndex >= queue.length) return; final targetItem = queue[targetIndex]; // 已有有效URL直接跳过,避免重复请求 if (targetItem.extras?['url'] != null && targetItem.extras!['url'].toString().startsWith(RegExp(r'http|https'))) { return; } // 替换为你自己的远程API请求逻辑,根据MediaItem的id/其他参数获取音频URL final fetchedAudioUrl = await yourCustomApiFetchMethod(itemId: targetItem.id); // 更新MediaItem的URL字段 final updatedItem = targetItem.copyWith( extras: {...?targetItem.extras, 'url': fetchedAudioUrl}, ); queue[targetIndex] = updatedItem; // 同步更新后台通知队列 await AudioServiceBackground.setQueue(queue); // 替换音频源对应位置的资源 await concatenatingAudioSource.setAt( targetIndex, AudioSource.uri(Uri.parse(fetchedAudioUrl), tag: updatedItem), ); // 如果更新的是当前播放项,同步更新通知栏显示 if (targetIndex == _player.currentIndex) { await AudioServiceBackground.setMediaItem(updatedItem); } }
4. 暴露自定义Action,支持任意队列项主动更新
重写onCustomAction方法,前端可以主动调用更新非播放状态的队列项:
@override Future<void> onCustomAction(String name, dynamic arguments) async { if (name == 'updateQueueItemUrl') { final targetIndex = arguments['index'] as int; await _updateQueueItemUrl(targetIndex); } }
前端调用示例:
// 主动更新索引为3的队列项URL,不管是否在播放都可以执行 AudioService.customAction('updateQueueItemUrl', {'index': 3});
5. 可选:添加自动预加载逻辑
监听播放器索引变化,自动加载当前项和下一项的URL,提升播放流畅度:
// 在onStart方法中添加监听 _player.currentIndexStream.listen((newIndex) { if (newIndex == null) return; // 加载当前播放项 _updateQueueItemUrl(newIndex); // 提前预加载下一项 if (newIndex + 1 < queue.length) { _updateQueueItemUrl(newIndex + 1); } });
注意事项
- 自行给
yourCustomApiFetchMethod添加错误处理和重试逻辑,避免请求失败导致播放错误 - 如果需要支持队列动态增删,直接调用
concatenatingAudioSource对应的add/insert/removeAt方法,同步更新queue数组即可 - 预加载的数量可以根据业务需求调整,比如队列很长可以提前加载前后各2项,平衡性能和流畅度
内容的提问来源于stack exchange,提问作者tolgatasci
相关产品推荐
相关产品推荐

