做外贸a货网站百度搜索推广多少钱
做外贸a货网站,百度搜索推广多少钱,南京金融网站建设,手机网页制作尺寸前言
欢迎加入开源鸿蒙跨平台社区#xff1a;https://openharmonycrossplatform.csdn.net
上一篇搞定了权限申请#xff0c;今天来讲语音识别引擎的创建——speechRecognizer.createEngine。这是整个语音识别流程中最关键的一步#xff0c;引擎创建成功了#xff0c;后面…前言欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net上一篇搞定了权限申请今天来讲语音识别引擎的创建——speechRecognizer.createEngine。这是整个语音识别流程中最关键的一步引擎创建成功了后面的监听、识别、停止都是顺水推舟的事。说实话createEngine这个API看起来很简单——就两个参数嘛。但实际用起来参数格式、异步处理、异常捕获、能力检测每一个环节都有讲究。我在适配过程中光是language参数的格式转换就折腾了好一会儿——Dart层传过来的是zh_CN下划线Core Speech Kit要求的是zh-CN连字符差一个符号引擎就创建失败。今天把activate方法中引擎创建相关的所有细节都讲透。本文对应源码FlutterSpeechPlugin.ets的activate方法第80-131行。一、speechRecognizer.createEngine 参数详解1.1 API签名speechRecognizer.createEngine(params:CreateEngineParams):PromiseSpeechRecognitionEngine这是一个异步方法返回Promise。必须用await等待引擎创建完成。1.2 CreateEngineParams 参数interfaceCreateEngineParams{language:string;// 识别语言BCP 47格式online:number;// 识别模式1在线0离线}flutter_speech中的调用this.asrEngineawaitspeechRecognizer.createEngine({language:language,// zh-CNonline:1// 在线识别});参数类型必填说明flutter_speech的值languagestring✅BCP 47语言代码由Dart层传入经convertLocale转换onlinenumber✅1在线0离线固定为11.3 为什么是异步的Android的SpeechRecognizer.createSpeechRecognizer()是同步的为什么OpenHarmony的createEngine是异步的原因是Core Speech Kit在创建引擎时需要做一些耗时操作检查系统AI服务是否可用加载语音识别模型建立与AI引擎服务的连接初始化音频采集管道如果这些操作是同步的会阻塞主线程导致UI卡顿。所以设计成异步是合理的。// ✅ 正确用await等待this.asrEngineawaitspeechRecognizer.createEngine({...});console.info(TAG,engine created);// 引擎已就绪// ❌ 错误不等待直接使用speechRecognizer.createEngine({...});// 返回Promise引擎还没创建好this.asrEngine.startListening(...);// asrEngine是undefined崩溃这也是为什么activate方法是async的因为内部需要await两个异步操作——权限申请和引擎创建。二、language 参数格式转换locale 到 BCP472.1 问题背景Dart层传过来的locale格式是下划线分隔的如zh_CN这是Dart/Flutter的标准格式。但Core Speech Kit要求的是BCP 47格式连字符分隔如zh-CN。Dart层 zh_CN en_US fr_FR Core Kit zh-CN en-US fr-FR2.2 convertLocale 实现flutter_speech的转换逻辑非常简洁privateconvertLocale(locale:string):string{returnlocale.replace(_,-);}就一行代码把下划线替换成连字符。2.3 为什么不用更复杂的转换你可能会想是不是应该做更完整的BCP 47解析比如处理zh-Hans-CN这种三段式格式实际上不需要。flutter_speech的Dart层只传两段式的locale如zh_CN、en_US而且Core Speech Kit目前只支持中文所以简单的replace就够了。但如果你在做一个更通用的插件可能需要更健壮的转换// 更健壮的转换flutter_speech不需要这么复杂privateconvertLocale(locale:string):string{// 处理各种可能的格式// zh_CN → zh-CN// zh → zh// zh_Hans_CN → zh-Hans-CNreturnlocale.replace(/_/g,-);// 全局替换所有下划线}注意flutter_speech用的是replace(_, -)不带g标志只替换第一个下划线。对于两段式locale来说足够了。如果有三段式locale需要用正则的全局替换replace(/_/g, -)。2.4 locale转换的调用位置// activate方法中的调用链constlanguagethis.convertLocale(locale);// zh_CN → zh-CNif(!this.isSupportedLocale(language)){// 语言不支持返回错误result.error(ERROR_LANGUAGE_NOT_SUPPORTED,...);return;}// 用转换后的language创建引擎this.asrEngineawaitspeechRecognizer.createEngine({language:language,// zh-CNonline:1});三、online 参数在线识别模式配置3.1 参数含义值模式网络要求准确率延迟1在线识别需要网络高取决于网络0离线识别不需要中等较低3.2 flutter_speech的选择flutter_speech硬编码了online: 1在线模式this.asrEngineawaitspeechRecognizer.createEngine({language:language,online:1// 固定在线模式});为什么选在线模式准确率更高在线模式使用云端大模型识别准确率明显优于离线词汇量更大云端模型的词汇覆盖面更广大多数场景有网络手机用户通常都有网络连接3.3 未来改进方向如果要支持用户选择在线/离线模式可以通过Dart层传参// Dart层未来可能的改进Futureactivate(Stringlocale,{bool onlinetrue})_channel.invokeMethod(speech.activate,{locale:locale,online:online,});// 原生端接收参数casespeech.activate:constargscall.argsasRecordstring,Object;constlocaleString(args[locale]);constonlineargs[online]asboolean;this.activate(locale,online?1:0,result);break;当前flutter_speech的Dart层只传一个locale字符串所以原生端用String(call.args)直接获取。如果要传多个参数需要改成Map格式。四、SystemCapability.AI.SpeechRecognizer 能力检测4.1 为什么需要能力检测不是所有OpenHarmony设备都支持语音识别。在创建引擎之前应该先检测设备是否具备这个能力if(!canIUse(SystemCapability.AI.SpeechRecognizer)){result.error(ERROR_NO_SPEECH_RECOGNITION_AVAILABLE,Device does not support speech recognition,null);return;}4.2 canIUse APIcanIUse是OpenHarmony的全局函数用于检测系统能力functioncanIUse(syscap:string):boolean参数说明示例syscap系统能力标识“SystemCapability.AI.SpeechRecognizer”返回true表示设备支持该能力false表示不支持。4.3 哪些设备可能不支持设备类型是否支持语音识别原因手机旗舰✅ 通常支持有AI芯片和麦克风手机入门⚠️ 可能不支持硬件能力不足平板✅ 通常支持和手机类似智慧屏⚠️ 取决于型号部分型号无麦克风穿戴设备❌ 通常不支持算力和存储不足开发板❌ 通常不支持无AI服务4.4 能力检测的位置flutter_speech把能力检测放在权限申请之后、引擎创建之前// 1. 权限申请// ...// 2. 能力检测 ← 在这里if(!canIUse(SystemCapability.AI.SpeechRecognizer)){result.error(ERROR_NO_SPEECH_RECOGNITION_AVAILABLE,...);return;}// 3. 语言校验// ...// 4. 引擎创建// ...为什么不把能力检测放在最前面因为即使设备支持语音识别没有权限也用不了。先检查权限可以更早地给用户反馈。不过这个顺序见仁见智。有人觉得应该先检测能力再申请权限——如果设备不支持就没必要弹权限弹窗了。两种方式都有道理flutter_speech选择了先权限后能力的顺序。五、引擎创建失败的异常处理与降级方案5.1 可能的失败原因createEngine可能因为多种原因失败抛出异常失败原因错误表现发生概率语言不支持异常language not supported高非中文时网络不可用异常network error中在线模式AI服务未启动异常service not available低系统资源不足异常resource exhausted极低引擎已存在异常engine already exists低5.2 flutter_speech的异常处理try{this.asrEngineawaitspeechRecognizer.createEngine({language:language,online:1});console.info(TAG,engine created successfully);this.setupListener();this.channel?.invokeMethod(speech.onSpeechAvailability,true);result.success(true);}catch(e){console.error(TAG,activate error:${JSON.stringify(e)});result.error(SPEECH_ACTIVATION_ERROR,Failed to activate speech recognition:${JSON.stringify(e)},null);}整个activate方法被try-catch包裹任何异常都会被捕获并通过result.error返回给Dart层。5.3 错误信息的序列化注意异常对象e的序列化方式// 用JSON.stringify序列化错误对象console.error(TAG,activate error:${JSON.stringify(e)});为什么用JSON.stringify而不是e.message因为OpenHarmony的异常对象结构可能和标准的Error不同JSON.stringify可以输出完整的错误信息包括错误码和详细描述。// 典型的错误对象结构{code:1002003,message:Language not supported}5.4 降级方案如果在线模式创建失败可以尝试降级到离线模式flutter_speech当前未实现但这是一个好的改进方向// 降级方案示例privateasynccreateEngineWithFallback(language:string):Promiseboolean{// 先尝试在线try{this.asrEngineawaitspeechRecognizer.createEngine({language:language,online:1});console.info(TAG,online engine created);returntrue;}catch(onlineErr){console.warn(TAG,online failed:${JSON.stringify(onlineErr)});}// 降级到离线try{this.asrEngineawaitspeechRecognizer.createEngine({language:language,online:0});console.info(TAG,offline engine created (fallback));returntrue;}catch(offlineErr){console.error(TAG,offline also failed:${JSON.stringify(offlineErr)});returnfalse;}}5.5 重复创建的处理如果用户多次调用activate需要先销毁旧引擎再创建新的// 当前flutter_speech没有显式处理这种情况// 建议改进privateasyncactivate(locale:string,result:MethodResult):Promisevoid{// 如果已有引擎先销毁if(this.asrEngine){console.info(TAG,destroying existing engine before creating new one);this.destroyEngine();}// 创建新引擎...}实际踩坑我测试时连续调用了两次activate第二次创建引擎时偶尔会失败。后来加了先销毁旧引擎的逻辑就好了。虽然Core Speech Kit理论上应该能处理这种情况但保险起见还是自己管理好引擎的生命周期。六、activate 方法的完整流程6.1 流程图activate(locale, result) │ ├── 1. 检查abilityContext │ └── null → error(SPEECH_CONTEXT_ERROR) → return │ ├── 2. 申请麦克风权限 │ └── denied → error(SPEECH_PERMISSION_DENIED) → return │ ├── 3. 检测设备能力 │ └── 不支持 → error(ERROR_NO_SPEECH_RECOGNITION_AVAILABLE) → return │ ├── 4. 转换locale格式 │ └── convertLocale(zh_CN) → zh-CN │ ├── 5. 校验语言支持 │ └── 不支持 → error(ERROR_LANGUAGE_NOT_SUPPORTED) → return │ ├── 6. 创建引擎 (await) │ └── 失败 → catch → error(SPEECH_ACTIVATION_ERROR) → return │ ├── 7. 设置监听器 │ └── setupListener() │ ├── 8. 通知Dart层 │ └── channel.invokeMethod(speech.onSpeechAvailability, true) │ └── 9. 返回成功 └── result.success(true)6.2 完整源码带注释privateasyncactivate(locale:string,result:MethodResult):Promisevoid{try{console.info(TAG,activate called with locale:${locale});// 第1步权限申请 if(this.abilityContext){console.info(TAG,requesting microphone permission...);constatManagerabilityAccessCtrl.createAtManager();constgrantResultawaitatManager.requestPermissionsFromUser(this.abilityContext,[ohos.permission.MICROPHONE]);constallGrantedgrantResult.authResults.every((status:number)statusabilityAccessCtrl.GrantStatus.PERMISSION_GRANTED);console.info(TAG,permission granted:${allGranted});if(!allGranted){result.error(SPEECH_PERMISSION_DENIED,Microphone permission denied,null);return;}}else{console.error(TAG,abilityContext is null);result.error(SPEECH_CONTEXT_ERROR,UIAbilityContext not available,null);return;}// 第2步能力检测 if(!canIUse(SystemCapability.AI.SpeechRecognizer)){result.error(ERROR_NO_SPEECH_RECOGNITION_AVAILABLE,Device does not support speech recognition,null);return;}// 第3步语言校验 constlanguagethis.convertLocale(locale);if(!this.isSupportedLocale(language)){result.error(ERROR_LANGUAGE_NOT_SUPPORTED,Language ${locale} is not supported on HarmonyOS.,null);return;}// 第4步创建引擎 console.info(TAG,creating engine with language:${language});this.asrEngineawaitspeechRecognizer.createEngine({language:language,online:1});console.info(TAG,engine created successfully);// 第5步设置监听器 this.setupListener();// 第6步通知Dart层 this.channel?.invokeMethod(speech.onSpeechAvailability,true);result.success(true);}catch(e){console.error(TAG,activate error:${JSON.stringify(e)});result.error(SPEECH_ACTIVATION_ERROR,Failed to activate speech recognition:${JSON.stringify(e)},null);}}6.3 各步骤的耗时分析步骤预估耗时是否异步说明权限申请0-5秒✅ await取决于用户操作速度能力检测1ms❌ 同步系统调用极快语言校验1ms❌ 同步字符串比较引擎创建500ms-3秒✅ await取决于网络和系统状态设置监听器1ms❌ 同步注册回调通知Dart1ms❌ 异步发送不等待结果整个activate方法的总耗时最快约500ms权限已授予引擎快速创建最慢可能超过5秒首次权限弹窗网络慢。Dart层应该在调用activate时显示loading状态。七、引擎创建成功后的操作7.1 setupListener引擎创建成功后立即设置监听器this.setupListener();这一步在下一篇第13篇会详细讲解。简单来说就是注册onStart、onResult、onComplete、onError四个回调。7.2 通知Dart层this.channel?.invokeMethod(speech.onSpeechAvailability,true);这行代码通过MethodChannel向Dart层发送一个事件告诉Dart层语音识别引擎已就绪。Dart层收到后会调用availabilityHandler(true)回调。7.3 返回结果result.success(true);最后通过result.success(true)告诉Dart层activate方法执行成功。Dart层的Future会以true完成。注意区分两种通知result.success(true)是对activate方法调用的直接响应同步语义channel.invokeMethod(speech.onSpeechAvailability, true)是一个异步事件通知。两者都需要因为Dart层可能分别监听方法返回值和事件回调。八、与Android引擎创建的对比8.1 代码对比Androidprivatevoidactivate(Stringlocale,MethodChannel.Resultresult){// 同步创建speechRecognizerSpeechRecognizer.createSpeechRecognizer(activity);speechRecognizer.setRecognitionListener(recognitionListener);// 检查是否可用if(SpeechRecognizer.isRecognitionAvailable(activity)){channel.invokeMethod(speech.onSpeechAvailability,true);result.success(true);}else{result.success(false);}}OpenHarmonyprivateasyncactivate(locale:string,result:MethodResult):Promisevoid{// 异步创建this.asrEngineawaitspeechRecognizer.createEngine({language:language,online:1});this.setupListener();this.channel?.invokeMethod(speech.onSpeechAvailability,true);result.success(true);}8.2 关键差异差异点AndroidOpenHarmony创建方式同步异步(await)语言参数不在创建时指定创建时指定在线/离线不在创建时指定创建时指定能力检测isRecognitionAvailable()canIUse()权限申请分离在另一个方法集成在activate中OpenHarmony的设计更前置语言和模式在创建引擎时就确定了而Android是在startListening时通过Intent指定。这意味着OpenHarmony如果要切换语言需要重新创建引擎。总结本文详细讲解了flutter_speech中语音识别引擎的创建过程createEngine参数languageBCP 47格式和online1在线0离线locale转换convertLocale将下划线格式转为连字符格式能力检测canIUse(SystemCapability.AI.SpeechRecognizer)异步创建必须用await等待引擎创建完成异常处理try-catch捕获所有异常通过result.error返回创建后操作setupListener 通知Dart层 返回成功下一篇我们深入语音识别监听器的实现——setupListener方法中四个回调的详细解析。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源Core Speech Kit createEngine文档canIUse系统能力检测Android SpeechRecognizer.createSpeechRecognizerflutter_speech OpenHarmony源码OpenHarmony AI能力概述开源鸿蒙跨平台社区Flutter-OHOS适配指南