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

SwiftUI获取服务器音频时长致应用冻结,求解决方法

问题:获取远程音频时长导致应用UI冻结

我一直在寻找显示服务器存储音频文件时长的方法,在Stack Overflow上找到一段代码,原本以为可行,但调用时会导致导航到对应视图时应用完全冻结。

原参考代码

let item = AVPlayerItem(url: url)
let duration = Double(item.asset.duration.value) / Double(item.asset.duration.timescale)

我的实现代码

获取时长的函数

func getDuration(url: URL) -> Double {
    let item = AVPlayerItem(url: url)
    let duration = Double(item.asset.duration.value) / Double(item.asset.duration.timescale)
    return duration
}

视图中的调用

Text(String(audioManager.getDuration(url: URL(string: "http://gameleprilucky.ru/soungs/" + (data.linkSoung ?? "abob"))!)))
    .font(.custom("Manrope-Medium", size: 12))
    .foregroundColor(.white)

问题原因

直接在主线程同步获取远程音频的时长,会触发网络请求和元数据加载的耗时操作,阻塞UI线程导致应用冻结。AVPlayerItem的asset默认不会自动加载远程资源的元数据,同步访问duration会强制触发同步加载,卡住主线程。

解决方法

必须通过异步加载音频元数据的方式处理,避免阻塞主线程。以下是几种可行方案:

方案1:用async/await封装异步函数(推荐,Swift 5.5+)

修改获取时长的函数为异步函数,利用AVAsset的异步加载API:

func getDuration(url: URL) async throws -> Double {
    let asset = AVAsset(url: url)
    // 异步加载duration元数据
    try await asset.load(.duration)
    let duration = asset.duration
    return Double(duration.value) / Double(duration.timescale)
}

在视图中通过Task异步调用,用状态变量更新UI:

struct AudioView: View {
    let data: YourDataType // 替换为你的数据类型
    @State private var audioDuration: Double? = nil
    let audioManager = YourAudioManager() // 替换为你的音频管理实例
    
    var body: some View {
        Text(audioDuration != nil ? String(format: "%.1f", audioDuration!) : "--")
            .font(.custom("Manrope-Medium", size: 12))
            .foregroundColor(.white)
            .task {
                guard let link = data.linkSoung,
                      let url = URL(string: "http://gameleprilucky.ru/soungs/" + link) else {
                    return
                }
                do {
                    audioDuration = try await audioManager.getDuration(url: url)
                } catch {
                    print("获取时长失败:\(error)")
                }
            }
    }
}

方案2:闭包回调(兼容旧版本Swift)

如果不支持async/await,用闭包异步返回结果:

func getDuration(url: URL, completion: @escaping (Double?) -> Void) {
    let asset = AVAsset(url: url)
    asset.loadValuesAsynchronously(forKeys: ["duration"]) {
        var error: NSError?
        let status = asset.statusOfValue(forKey: "duration", error: &error)
        DispatchQueue.main.async {
            switch status {
            case .loaded:
                let duration = asset.duration
                completion(Double(duration.value) / Double(duration.timescale))
            case .failed, .cancelled:
                print("加载时长失败:\(error?.localizedDescription ?? "未知错误")")
                completion(nil)
            default:
                completion(nil)
            }
        }
    }
}

在视图中调用并更新状态:

struct AudioView: View {
    let data: YourDataType
    @State private var audioDuration: Double? = nil
    let audioManager = YourAudioManager()
    
    var body: some View {
        Text(audioDuration != nil ? String(format: "%.1f", audioDuration!) : "--")
            .font(.custom("Manrope-Medium", size: 12))
            .foregroundColor(.white)
            .onAppear {
                guard let link = data.linkSoung,
                      let url = URL(string: "http://gameleprilucky.ru/soungs/" + link) else {
                    return
                }
                audioManager.getDuration(url: url) { duration in
                    self.audioDuration = duration
                }
            }
    }
}

关键注意事项

  • 绝对不要在主线程执行网络请求或元数据加载这类耗时操作,必须异步处理。
  • 确保元数据加载完成后再访问duration,避免触发同步阻塞。
  • 用视图状态变量存储时长,异步获取结果后再更新UI,保证界面流畅。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 21:30:54