关于 maui-speech-to-text
面向 .NET MAUI 开发者的语音转文字实践技能,涵盖 CommunityToolkit.Maui 的权限、监听和事件处理注意事项。提供跨平台常见问题与服务封装建议。
主要功能
- 01识别前检查麦克风和语音权限
- 02为监听设置超时与取消令牌
- 03及时取消订阅事件处理程序
- 04通过部分结果显示实时转写
- 05将语音识别封装为服务
适用场景
- 01避免 iOS 缺少权限说明导致崩溃
- 02处理 Android 麦克风权限拒绝
- 03防止监听耗电及重复事件处理
- 04在 MAUI 界面中展示实时转写
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-speech-to-text安装后在 Claude Code 中输入/maui-speech-to-text确认已加载。
附带文件references/speech-to-text-api.md
详细介绍
语音转文本 — 注意事项与最佳实践
有关完整的服务实现、类型和 UI 集成模式,请参阅 references/speech-to-text-api.md。
关键事项:权限处理
开始语音识别之前,务必先请求权限。麦克风和语音识别权限都必须获得。
// ❌ Starting recognition without checking permissions
var result = await _speechService.StartListeningAsync();
// ✅ Always check permissions first
if (!await _speechService.RequestPermissionsAsync())
return; // Gracefully handle denial
var result = await _speechService.StartListeningAsync(cancellationToken);
⚠️ iOS 需要同时配置两项权限说明
如果 Info.plist 中缺少 NSSpeechRecognitionUsageDescription 或 NSMicrophoneUsageDescription,就会导致运行时崩溃,而不是优雅地失败。
⚠️ Android:重新请求权限
在 Android 上,如果用户两次拒绝 RECORD_AUDIO 权限,操作系统就不会再显示权限提示。你必须引导用户手动前往设置。
常见错误
❌ 未设置超时 — 无限时长聆听
务必设置超时,以免聆听会话无限持续并耗尽电池电量:
// ❌ No timeout — listens forever if no speech detected
await _speechToText.StartListenAsync(options, CancellationToken.None);
// ✅ Use a combined timeout + user cancellation token
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(60));
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(
userCancellationToken, timeoutCts.Token);
await _speechToText.StartListenAsync(options, combinedCts.Token);
❌ 未取消订阅事件处理程序
事件订阅未清理会导致重复处理和内存泄漏:
// ❌ Subscribe without unsubscribe
_speechToText.RecognitionResultUpdated += OnRecognitionResultUpdated;
// ✅ Always unsubscribe in finally block
try
{
_speechToText.RecognitionResultUpdated += OnRecognitionResultUpdated;
// ... listen ...
}
finally
{
_speechToText.RecognitionResultUpdated -= OnRecognitionResultUpdated;
}
❌ 未释放 CancellationTokenSource
// ❌ Leaked CTS
_currentCts = new CancellationTokenSource();
// ✅ Dispose in finally
try { /* ... */ }
finally
{
_currentCts?.Dispose();
_currentCts = null;
}
平台注意事项
| 平台 | 注意事项 |
|---|---|
| iOS | 缺少任一 plist 键 → 运行时崩溃 |
| Android | 用户两次拒绝权限 → 操作系统不再显示提示;必须引导用户前往设置 |
| 所有平台 | 未设置超时 → 无限时长聆听会耗尽电池电量 |
| 所有平台 | 已在聆听时调用 StartListeningAsync → 返回错误,而不是抛出异常 |
架构建议
-
将
ISpeechToText封装在服务中 — 不要在 ViewModel 中直接使用SpeechToText.Default。将其封装为ISpeechRecognitionService,以便进行测试和状态管理。 -
使用部分结果改善 UX — 订阅
PartialResultReceived,以实时反馈转录结果。用户期望看到文字随着说话逐渐出现。 -
连续聆听 = 带延迟的循环 — 在对话模式下,通过带短暂延迟(
Task.Delay(100))的循环调用StartListeningAsync。 -
防止重复启动 — 启动前检查状态:
if (State == SpeechRecognitionState.Listening) return new SpeechRecognitionResultDto { Success = false, ErrorMessage = "Already listening" }; -
自然语言输出 — CommunityToolkit.Maui 返回经过规范化并带有标点的文本,而不是原始音素。基本用例无需后处理。
-
与 UI 无关的服务 —
ISpeechRecognitionService模式在 XAML/MVVM、C# Markup 和 MauiReactor 中的用法完全相同。有关这三种模式,请参阅references/speech-to-text-api.md。
检查清单
- 已安装
CommunityToolkit.MauiNuGet 包(查找当前版本) - 已在
MauiProgram.cs中调用UseMauiCommunityToolkit() - 已通过 DI 将
ISpeechToText注册为 singleton - iOS:Info.plist 中同时包含
NSSpeechRecognitionUsageDescription和NSMicrophoneUsageDescription - Android:AndroidManifest.xml 中包含
RECORD_AUDIO权限 - 每次调用
StartListeningAsync前都检查权限 - 已配置超时(建议最长 60 秒)
- 已在
finally块中取消订阅事件处理程序 - 使用后已释放
CancellationTokenSource