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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 15:27:00