3.2 KiB
3.2 KiB
AudioService 取消语义修复说明
日期:2026-05-20
范围:My project/Assets/FlowScope/Runtime/Audio/AudioService.cs
优先级:P0 小修复
结论:建议立即顺手修复
问题概述
AudioService.PlayBgmAsync 和 AudioService.PlaySfxAsync 支持传入 CancellationToken,但当前内部 LoadClipAsync 会捕获所有异常,并统一包装为 InvalidOperationException。
这会导致调用方主动取消音频加载时,拿到的不是 OperationCanceledException,而是普通失败异常。
影响
取消语义被破坏后,调用方无法可靠区分以下两类情况:
- 生命周期或用户操作导致的正常取消。
- 资源缺失、Addressables 失败、类型不匹配等真实加载错误。
可能造成的问题:
- 正常取消被记录成错误日志。
- UI 或业务层误判为资源加载失败。
- 后续重试、错误兜底、统计埋点出现噪音。
- 框架内不同服务的取消语义不一致,增加异步生命周期排查成本。
推荐修改
在 AudioService.LoadClipAsync 中单独保留 OperationCanceledException,不要包装。
目标代码形态:
private async Task<IResourceHandle<AudioClip>> LoadClipAsync(
string key,
CancellationToken cancellationToken)
{
try
{
var handle = await _resources.LoadAsync<AudioClip>(key, cancellationToken);
return ValidateLoadedClip(key, handle);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception exception)
{
throw new InvalidOperationException($"Failed to load audio clip '{key}'.", exception);
}
}
测试建议
在 AudioServiceTests 中补充异步取消测试。
建议覆盖:
PlayBgmAsync_WhenCanceled_ThrowsOperationCanceledExceptionPlaySfxAsync_WhenCanceled_ThrowsOperationCanceledException
测试要点:
- 创建
CancellationTokenSource。 - 调用
Cancel()。 - 调用
PlayBgmAsync或PlaySfxAsync。 - 断言抛出
OperationCanceledException。 - 断言没有创建新的
AudioSource。
示例结构:
[UnityTest]
public IEnumerator PlayBgmAsync_WhenCanceled_ThrowsOperationCanceledException()
{
var service = CreateService();
using var cts = new CancellationTokenSource();
cts.Cancel();
var sourceCountBeforePlay = Object.FindObjectsOfType<AudioSource>().Length;
yield return ThrowsAsync<OperationCanceledException>(
service.PlayBgmAsync("bgm-a", cancellationToken: cts.Token));
Assert.That(Object.FindObjectsOfType<AudioSource>().Length, Is.EqualTo(sourceCountBeforePlay));
}
如果现有测试工具方法只支持 Func<Task>,也可以按当前测试文件风格新增一个接收 Task 的 ThrowsAsync<TException> helper。
验收标准
PlayBgmAsync在取消时抛出OperationCanceledException。PlaySfxAsync在取消时抛出OperationCanceledException。- 取消时不创建
AudioSource。 - 取消时不把异常包装成
InvalidOperationException。 - 现有 PlayMode 音频测试继续通过。
后续备注
该问题不是架构阻塞项,但修复成本很低。建议本轮直接修掉,避免后续异步服务取消语义不一致。