Flutter三方库适配OpenHarmony【flutter_speech】— 错误处理与异常恢复

📅 发布时间:2026/7/4 21:45:38 👁️ 浏览次数:
Flutter三方库适配OpenHarmony【flutter_speech】— 错误处理与异常恢复
前言欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net前20篇我们把flutter_speech的核心功能都讲完了从第21篇开始进入进阶实践阶段。今天先讲错误处理——这是决定插件能不能用和好不好用之间的关键差距。一个没有错误处理的插件在理想环境下跑得很好一到真实场景就各种崩溃。网络断了怎么办权限被拒了怎么办引擎创建失败怎么办这些问题都需要优雅地处理。本文重点梳理flutter_speech中所有的错误处理路径分析每种错误的成因和恢复策略。一、常见错误码分类与含义1.1 flutter_speech自定义错误码错误码触发位置含义严重程度SPEECH_PERMISSION_DENIEDactivate麦克风权限被拒绝⭐⭐⭐⭐⭐SPEECH_CONTEXT_ERRORactivateUIAbilityContext不可用⭐⭐⭐⭐⭐ERROR_NO_SPEECH_RECOGNITION_AVAILABLEactivate设备不支持语音识别⭐⭐⭐⭐⭐ERROR_LANGUAGE_NOT_SUPPORTEDactivate语言不支持⭐⭐⭐⭐SPEECH_ACTIVATION_ERRORactivate引擎创建失败通用⭐⭐⭐⭐ERROR_ENGINE_NOT_INITIALIZEDstartListening引擎未初始化⭐⭐⭐ERROR_SPEECH_LISTENstartListening启动监听失败⭐⭐⭐1.2 Core Speech Kit引擎错误码这些错误码通过onError回调传递错误码含义常见原因0无错误-1网络超时网络不稳定2网络异常无网络连接3音频异常麦克风被占用4引擎忙上一次识别未结束5无语音输入VAD超时用户没说话6识别失败服务端错误1.3 错误分类按可恢复性分类类别错误恢复方式不可恢复设备不支持、权限永久拒绝提示用户禁用功能需用户操作权限首次拒绝、无网络引导用户操作后重试自动恢复网络超时、引擎忙、无语音输入自动重试二、引擎未初始化错误处理2.1 错误场景用户在没有调用activate的情况下直接调用listenprivatestartListening(result:MethodResult):void{if(!this.asrEngine){result.error(ERROR_ENGINE_NOT_INITIALIZED,Speech engine not initialized. Call activate first.,null);return;}// ...}2.2 Dart层的处理_speech.listen().then((result){setState(()_isListeningresult);}).catchError((e){if(eisPlatformExceptione.codeERROR_ENGINE_NOT_INITIALIZED){// 自动尝试激活_speech.activate(zh_CN).then((_)_speech.listen());}});2.3 防御性设计flutter_speech的示例App通过UI状态来防止这种错误——_speechRecognitionAvailable为false时Listen按钮不可点击。这是前端防御比后端报错更好的用户体验。三、权限拒绝场景的优雅降级3.1 权限拒绝的两种情况情况表现处理方式首次拒绝用户点击拒绝提示并引导重新授权永久拒绝用户勾选不再询问后拒绝引导用户去系统设置3.2 原生端的处理constgrantResultawaitatManager.requestPermissionsFromUser(this.abilityContext,[ohos.permission.MICROPHONE]);constallGrantedgrantResult.authResults.every((status:number)statusabilityAccessCtrl.GrantStatus.PERMISSION_GRANTED);if(!allGranted){result.error(SPEECH_PERMISSION_DENIED,Microphone permission denied,null);return;}3.3 Dart层的降级策略_speech.activate(zh_CN).then((res){setState(()_speechRecognitionAvailableres);}).catchError((e){if(eisPlatformException){switch(e.code){caseSPEECH_PERMISSION_DENIED:_showPermissionDeniedDialog();break;caseERROR_NO_SPEECH_RECOGNITION_AVAILABLE:_showDeviceNotSupportedMessage();break;default:_showGenericErrorMessage(e.message??Unknown error);}}setState(()_speechRecognitionAvailablefalse);});3.4 引导用户去设置页void_showPermissionDeniedDialog(){showDialog(context:context,builder:(ctx)AlertDialog(title:Text(需要麦克风权限),content:Text(语音识别需要麦克风权限才能工作。请在系统设置中开启。),actions:[TextButton(onPressed:()Navigator.pop(ctx),child:Text(取消),),TextButton(onPressed:(){Navigator.pop(ctx);// OpenHarmony打开应用设置页// 需要通过MethodChannel调用原生端的设置跳转},child:Text(去设置),),],),);}四、网络异常对在线识别的影响4.1 网络异常的表现flutter_speech使用在线识别模式online: 1网络异常会导致阶段网络异常的影响createEngine引擎创建失败抛异常startListening可能成功音频采集不需要网络识别过程中onError回调错误码1或24.2 引擎创建时的网络异常try{this.asrEngineawaitspeechRecognizer.createEngine({language:language,online:1});}catch(e){// 网络异常时createEngine可能抛出异常console.error(TAG,activate error:${JSON.stringify(e)});result.error(SPEECH_ACTIVATION_ERROR,Failed to activate speech recognition:${JSON.stringify(e)},null);}4.3 识别过程中的网络异常onError(sessionId:string,errorCode:number,errorMessage:string):void{console.error(TAG,onError: code${errorCode}, message${errorMessage});plugin.isListeningfalse;channel?.invokeMethod(speech.onSpeechAvailability,false);channel?.invokeMethod(speech.onError,errorCode);}错误码1网络超时和2网络异常都会触发onError。4.4 网络恢复后的重试voiderrorHandler()activateSpeechRecognizer();flutter_speech示例App的策略是自动重新初始化。网络恢复后重新activate通常能成功。改进建议可以在重试前先检查网络状态避免在无网络时反复重试voiderrorHandler()async{// 等待一段时间再重试awaitFuture.delayed(Duration(seconds:2));activateSpeechRecognizer();}五、errorHandler 重新激活策略分析5.1 当前实现voiderrorHandler()activateSpeechRecognizer();示例App的错误处理策略极其简单——无条件重新初始化。5.2 执行流程onError触发 → Dart: errorHandler() → activateSpeechRecognizer() → _speech SpeechRecognition() → 设置所有回调 → activate(zh_CN) → 成功 → _speechRecognitionAvailable true → 失败 → _speechRecognitionAvailable false (不会再次触发errorHandler因为activate失败不走onError)5.3 潜在问题无限重试循环如果错误在识别过程中持续发生比如麦克风硬件故障会出现listen → onError → errorHandler → activate → listen → onError → errorHandler → ...不过实际上这个循环不会真的无限——因为errorHandler只调用activateSpeechRecognizer不会自动调用listen。用户需要手动点击Listen按钮才会再次开始识别。5.4 改进方案带退避的重试int _retryCount0;staticconstint _maxRetries3;staticconstListint_retryDelays[1000,3000,10000];// 毫秒voiderrorHandler()async{if(_retryCount_maxRetries){setState(()_speechRecognitionAvailablefalse);_showErrorSnackBar(语音识别暂时不可用请稍后再试);_retryCount0;return;}finaldelay_retryDelays[_retryCount];_retryCount;awaitFuture.delayed(Duration(milliseconds:delay));activateSpeechRecognizer();}// 在activate成功时重置计数voidonSpeechAvailability(bool result){if(result)_retryCount0;setState(()_speechRecognitionAvailableresult);}这个方案的优点有限重试最多重试3次退避延迟每次重试间隔递增1秒、3秒、10秒用户反馈超过最大重试次数后提示用户自动重置成功后重置计数器5.5 各错误的推荐恢复策略错误推荐策略重试次数用户提示权限拒绝不重试引导设置0弹窗引导设备不支持不重试0永久提示语言不支持不重试切换语言0SnackBar网络超时自动重试3次检查网络网络异常等待网络恢复3次检查网络音频异常自动重试1次检查麦克风引擎忙延迟重试2次稍后再试无语音输入不重试正常行为0提示说话六、try-catch 在插件中的使用模式6.1 flutter_speech中的try-catch分布方法有try-catchcatch中的行为activate✅result.error 日志startListening✅result.error 重置状态 日志cancel✅result.success(true) 日志stop✅result.success(true) 日志destroyEngine✅仅日志6.2 两种catch策略策略A报告错误activate、startListeningcatch(e){result.error(ERROR_CODE,message:${JSON.stringify(e)},null);}策略B静默成功cancel、stopcatch(e){console.error(TAG,error:${JSON.stringify(e)});result.success(true);// 即使出错也返回成功}选择哪种策略取决于操作的语义activate和listen是请求做某事失败了应该告诉调用方cancel和stop是请求停止即使底层出错从用户角度来说停止已经完成了6.3 JSON.stringify序列化错误console.error(TAG,error:${JSON.stringify(e)});为什么用JSON.stringify而不是e.message因为OpenHarmony的异常对象可能不是标准的Error类型JSON.stringify能输出完整的错误信息{code:1002003,message:Language not supported}七、错误处理的完整流程图Dart调用activate(zh_CN) │ ├── abilityContext null? │ └── 是 → SPEECH_CONTEXT_ERROR → Dart catchError │ ├── 权限申请 │ └── 拒绝 → SPEECH_PERMISSION_DENIED → Dart catchError │ ├── canIUse检测 │ └── 不支持 → ERROR_NO_SPEECH_RECOGNITION_AVAILABLE → Dart catchError │ ├── 语言校验 │ └── 不支持 → ERROR_LANGUAGE_NOT_SUPPORTED → Dart catchError │ ├── createEngine │ └── 异常 → SPEECH_ACTIVATION_ERROR → Dart catchError │ └── 成功 → result.success(true) → Dart then Dart调用listen() │ ├── asrEngine null? │ └── 是 → ERROR_ENGINE_NOT_INITIALIZED → Dart catchError │ ├── startListening │ └── 异常 → ERROR_SPEECH_LISTEN → Dart catchError │ └── 成功 → result.success(true) → Dart then 识别过程中 │ ├── 正常 → onResult → speech.onSpeech → Dart回调 │ └── 异常 → onError → speech.onError → Dart errorHandler → speech.onSpeechAvailability(false)八、最佳实践总结8.1 原生端每个公开方法都要try-catch防止未捕获异常导致崩溃错误码要有意义让Dart层能根据错误码做不同处理错误信息要包含上下文包含locale、sessionId等信息便于调试销毁方法绝不抛异常catch后仅记录日志8.2 Dart端区分错误类型根据错误码做不同的恢复策略有限重试设置最大重试次数避免无限循环用户反馈每种错误都应该有对应的UI提示前端防御通过UI状态防止无效操作比后端报错更好8.3 检查清单所有MethodCall分支都有result响应所有异步操作都有try-catch错误码命名清晰、有文档Dart层对每种错误码有对应处理重试策略有上限用户能看到有意义的错误提示总结本文梳理了flutter_speech中所有的错误处理路径7个自定义错误码覆盖权限、能力、语言、引擎、监听等场景6个引擎错误码网络、音频、引擎状态等运行时错误两种catch策略关键操作报告错误停止操作静默成功errorHandler自动恢复简单但有效建议加重试限制权限降级区分首次拒绝和永久拒绝引导用户操作下一篇我们讲调试技巧与日志分析——如何高效地定位和解决flutter_speech中的问题。如果这篇文章对你有帮助欢迎点赞、收藏、关注你的支持是我持续创作的动力相关资源Core Speech Kit错误码文档Flutter PlatformException文档OpenHarmony权限管理flutter_speech OpenHarmony源码开源鸿蒙跨平台社区