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

如何用Swift Concurrency异步加载AVQueuePlayer避免主线程阻塞?

解决大量音频资源异步加载到AVQueuePlayer的问题

针对你的场景(iOS 16+,200个短音频资源,需无阻塞加载并保持播放顺序),以下是正确的实现方案:

核心问题分析

  1. 同步加载的问题:直接通过urls.map(AVAsset.init).map(AVPlayerItem.init)创建资源时,AVAsset会同步尝试读取媒体元数据,大量资源会阻塞主线程。
  2. 你之前异步代码的错误:
    • TaskGroup的遍历逻辑错误,不应在添加任务的循环内遍历结果
    • 未维护原始URL的顺序,并行加载完成的资源顺序随机,无法支持段落跳转
    • 错误使用可选链式调用player?.insert(player为非可选实例)

正确实现代码

let audioURLs: [URL] = // 你的音频URL数组
let queuePlayer = AVQueuePlayer()

// 在后台异步执行加载逻辑,不阻塞主线程
Task {
    do {
        // 创建任务组,并行加载每个音频资源,同时记录原始索引以保证顺序
        let sortedLoadedItems = try await withThrowingTaskGroup(of: (index: Int, item: AVPlayerItem).self) { group in
            // 为每个URL启动加载任务
            for (index, url) in audioURLs.enumerated() {
                group.addTask {
                    let asset = AVAsset(url: url)
                    // 异步加载必要资源属性(触发媒体数据预加载,避免播放时阻塞)
                    _ = try await asset.load(.duration)
                    let playerItem = AVPlayerItem(asset: asset)
                    // 可选:加载播放状态,确保资源就绪
                    _ = try await playerItem.load(.playbackStatus)
                    return (index, playerItem)
                }
            }
            
            // 收集所有加载完成的结果
            var loadedResults = [(index: Int, item: AVPlayerItem)]()
            for try await result in group {
                loadedResults.append(result)
            }
            
            // 按原始索引排序,保证播放顺序与URL列表一致
            return loadedResults.sorted(by: { $0.index < $1.index }).map(\.item)
        }
        
        // 回到主线程更新AVQueuePlayer(AVFoundation操作需在主线程执行)
        await MainActor.run {
            sortedLoadedItems.forEach { item in
                queuePlayer.insert(item, after: nil)
            }
        }
        
        // 可选:启动播放
        queuePlayer.play()
    } catch {
        // 处理加载错误(如个别资源加载失败)
        print("资源加载失败:\(error.localizedDescription)")
    }
}

关键细节说明

  • 并行加载与顺序保证:通过TaskGroup并行加载每个资源,同时记录原始索引,加载完成后按索引排序,确保队列顺序与原始URL列表一致,满足跳转指定段落的需求。
  • 异步资源预加载:调用asset.load(.duration)和playerItem.load(.playbackStatus)触发异步加载,避免后续播放时的主线程阻塞。
  • 主线程安全:所有对AVQueuePlayer的修改操作通过MainActor.run回到主线程执行,符合AVFoundation的线程安全要求。

可选优化

  • 若需支持加载进度展示:可在TaskGroup内部维护已完成任务数,通过@MainActor标记的属性更新UI进度。
  • 若需跳过失败资源:可将任务组的返回类型改为(index: Int, item: AVPlayerItem?),在任务内部捕获错误并返回nil,最后过滤掉nil项。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 13:55:14