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

Flutter中使用just_audio加载大音频播放列表优化方案咨询

优化Flutter音乐应用播放列表加载速度问题

我使用just_audio和just_audio_background开发了一款Flutter音乐应用,音乐功能及控件运行正常,但加载包含至少20首长时长歌曲的播放列表时耗时过长。当前实现方式如下:

//Generating list
list.add(AudioSource.uri(
    Uri.parse(musicDetails!.trackFileUrl!),
        tag: MediaItem(
            id: currentSongIndex.toString(),
             album: musicDetails!.description ?? "",
             title: musicDetails!.title ?? "",
             artUri: Uri.parse(musicDetails!.coverImage ?? ""),
           ),
       )
);
//Adding entire list to playlist
final playlist = ConcatenatingAudioSource(children: list);
await player.setLoopMode(LoopMode.all);
await player.setAudioSource(playlist,initialIndex: currentSongIndex.value);

我希望修改当前向播放器添加播放列表的实现方式,请问该如何实现以缩短加载时间?


优化方案

1. 动态预加载,避免一次性创建所有音频源

ConcatenatingAudioSource支持动态增删子项,不需要一次性初始化全部歌曲的AudioSource。可以先创建空的拼接源,只预加载当前播放歌曲及前后少量歌曲,剩余歌曲在用户切换曲目或滑动列表时再动态添加,同时移除超出预加载范围的旧源节省资源。

示例代码:

// 初始化空的拼接播放列表
final playlist = ConcatenatingAudioSource(children: []);
await player.setAudioSource(playlist, initialIndex: 0);
await player.setLoopMode(LoopMode.all);

// 定义预加载范围:当前索引前后各3首
void preloadNearbySongs(int currentIndex) {
  final totalSongs = allMusicDetails.length;
  final start = max(0, currentIndex - 3);
  final end = min(totalSongs - 1, currentIndex + 3);

  // 添加需要预加载的歌曲
  for (int i = start; i <= end; i++) {
    bool exists = playlist.children.any((source) => 
        (source.tag as MediaItem).id == i.toString());
    if (!exists) {
      final audioSource = AudioSource.uri(
        Uri.parse(allMusicDetails[i].trackFileUrl!),
        tag: MediaItem(
          id: i.toString(),
          album: allMusicDetails[i].description ?? "",
          title: allMusicDetails[i].title ?? "",
          artUri: Uri.parse(allMusicDetails[i].coverImage ?? ""),
        ),
      );
      playlist.insert(i, audioSource);
    }
  }

  // 清理超出预加载范围的旧音频源
  final cleanStart = max(0, currentIndex - 6);
  final cleanEnd = min(totalSongs - 1, currentIndex + 6);
  // 移除前面的旧源
  for (int i = 0; i < cleanStart; i++) {
    if (i < playlist.children.length) playlist.removeAt(i);
  }
  // 移除后面的旧源
  for (int i = playlist.children.length - 1; i > cleanEnd; i--) {
    playlist.removeAt(i);
  }
}

// 监听播放器当前索引变化,触发预加载
player.currentIndexStream.listen((index) {
  if (index != null) preloadNearbySongs(index);
});

// 首次加载当前选中的歌曲及附近曲目
preloadNearbySongs(currentSongIndex.value);

2. 自定义懒加载音频源

通过继承AudioSource实现懒加载逻辑,只有当播放器需要播放某首歌时,才去真正创建解析AudioSource.uri,避免一次性解析所有歌曲的URI和元数据。

示例代码:

class LazyAudioSource extends AudioSource {
  final MusicDetails musicDetails;
  final int index;

  LazyAudioSource(this.musicDetails, this.index);

  @override
  Future<AudioSource> resolve() async {
    // 仅在需要播放时才创建实际的音频源
    return AudioSource.uri(
      Uri.parse(musicDetails.trackFileUrl!),
      tag: MediaItem(
        id: index.toString(),
        album: musicDetails.description ?? "",
        title: musicDetails.title ?? "",
        artUri: Uri.parse(musicDetails.coverImage ?? ""),
      ),
    );
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is LazyAudioSource &&
          runtimeType == other.runtimeType &&
          index == other.index;

  @override
  int get hashCode => index.hashCode;
}

// 使用懒加载源初始化播放列表
final playlist = ConcatenatingAudioSource(
  children: allMusicDetails
      .asMap()
      .entries
      .map((entry) => LazyAudioSource(entry.value, entry.key))
      .toList(),
);

await player.setAudioSource(playlist, initialIndex: currentSongIndex.value);
await player.setLoopMode(LoopMode.all);

3. 分离封面图片加载逻辑

封面图片的同步解析会额外增加AudioSource的创建耗时,可以暂时不在MediaItem中设置artUri,等到UI需要显示封面时再异步加载并本地缓存,避免阻塞播放列表的初始化流程。


内容的提问来源于stack exchange,提问作者Khyati Modi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 19:42:40