谷歌SEO

谷歌SEO

Products

当前位置:首页 > 谷歌SEO >

如何在OpenHarmony环境下使用Flutter库实现语音识别的停止与取消功能?

96SEO 2026-02-20 07:35 14


如何在OpenHarmony环境下使用Flutter库实现语音识别的停止与取消功能?

xmlns="http://www.w3.org/2000/svg"

style="display:

none;">

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

语音识别不只是"开始"那么简单,怎么结束同样重要。

flutter_speech提供了两种结束方式——stopcancel,它们的语义完全不同:stop是"我说完了,给我结果",cancel是"算了不要了"。

这两个方法的代码都很短,加起来不到30行。

但别小看这30行代码,里面涉及的状态管理边界场景处理是整个插件中最容易出bug的地方。

重复调用怎么办?引擎未初始化怎么办?正在识别中cancel了,结果回调还会触发吗?

我在测试阶段遇到了好几个和停止/取消相关的问题,今天把这些经验都整理出来。

💡本文对应源码FlutterSpeechPlugin.etsstop方法(第237-248行)和cancel方法(第224-235行)。

/>

一、stop(finish)与

cancel

核心区别

操作Dart层方法原生层API语义是否返回结果
停止_speech.stop()asrEngine.finish(sessionId)“我说完了”

返回最终结果

取消_speech.cancel()asrEngine.cancel(sessionId)“不要了”

不返回结果

1.2

行为差异

stop(finish)

用户说"今天天气怎么样"

点击stop

返回最终识别结果:"今天天气怎么样"

├──

触发

cancel

用户说"今天天气怎么样"

点击cancel

可能触发(但无结果)

1.3

类比理解

把语音识别比作拍照

  • stop=

    按下快门,等照片保存完成

  • cancel=

    关闭相机,不拍了

或者比作写邮件

  • stop=

    点击"发送"

  • cancel=

    点击"丢弃草稿"

1.4

三平台的命名对比

操作AndroidiOSOpenHarmony
正常停止stopListening()endAudio()finish(sessionId)
取消cancel()(task.cancel())cancel(sessionId)

📌命名差异:Android叫stopListening,OpenHarmony叫finish

虽然名字不同,但语义一致——都是"正常结束并获取结果"。

二、finish

方法:正常结束并获取最终结果

2.1

源码实现

privatestop(result:MethodResult):void{try{if(this.asrEngine&&this.isListening){this.asrEngine.finish(this.sessionId);this.isListening=false;}result.success(true);}catch(e){console.error(TAG,`stop

error:${JSON.stringify(e)}`);result.success(true);}}

2.2

逐行解析

第1行if

(this.asrEngine

this.isListening)

  • 双重检查:引擎存在正在监听
  • 如果引擎不存在或没在监听,直接跳过,返回success

第2行this.asrEngine.finish(this.sessionId)

  • 调用Core

    Speech

    Kit的finish方法

  • 传入sessionId指定要停止的会话
  • 引擎会处理剩余音频并触发最终结果回调

第3行this.isListening

=

false

  • 立即将状态标记为"未监听"
  • 不等回调触发再改状态,避免竞态条件

第4行result.success(true)

  • 无论是否执行了finish,都返回success
  • 这是因为"停止"操作本身不应该失败——即使没在监听,停止也是合理的

2.3

为什么catch中也返回success

catch(e){console.error(TAG,`stop

error:${JSON.stringify(e)}`);result.success(true);//

错误了也返回success?}

这是一个设计决策:stop操作的语义是"请求停止",即使底层出了异常,从用户的角度来说"停止"这个动作已经完成了。

返回error会让Dart层的Future抛异常,可能导致UI出现不必要的错误提示。

🤔我的看法:这种处理方式有争议。

有人认为应该把错误传回去让调用方知道。

但在实际使用中,stop失败通常是因为引擎已经自己停了(比如VAD超时),这时候返回error反而会让用户困惑。

所以flutter_speech选择了"静默成功"的策略。

2.4

finish后的回调时序

调用finish后,引擎会触发以下回调:

asrEngine.finish(sessionId)

├──

引擎处理剩余音频(可能需要几百毫秒)

├──

speech.onRecognitionComplete(text)

└──

(isListening已经是false,跳过)

⚠️注意finish是异步的——调用后不会立即触发回调。

引擎需要时间处理剩余音频。

所以result.success(true)是在回调之前返回的,Dart层收到success后还需要等待onRecognitionComplete回调才能拿到最终结果。

三、cancel

方法:立即中断不返回结果

3.1

源码实现

privatecancel(result:MethodResult):void{try{if(this.asrEngine&&this.isListening){this.asrEngine.cancel(this.sessionId);this.isListening=false;}result.success(true);}catch(e){console.error(TAG,`cancel

error:${JSON.stringify(e)}`);result.success(true);}}

3.2

与stop的代码差异

把两个方法放在一起对比:

//

stopthis.asrEngine.finish(this.sessionId);//

cancelthis.asrEngine.cancel(this.sessionId);//

唯一的区别

代码结构完全一样,唯一的区别就是调用的API不同:finishvscancel

3.3

cancel后的回调行为

asrEngine.cancel(sessionId)

├──

(isListening已经是false,跳过)

└──

关键区别

cancel后不会触发onResult(isLast=true),所以Dart层不会收到speech.onRecognitionComplete事件。

这正是cancel的语义——“不要结果了”。

3.4

cancel的使用场景

场景说明
用户点击"取消"按钮用户主动放弃本次识别
切换语言后重新识别先cancel旧的,再start新的
页面退出离开语音识别页面时清理
防重入startListening中先cancel旧会话

四、isListening

isListening的生命周期

privateisListening:boolean=false;

isListening在以下位置被修改:

位置操作新值说明
startListening开始识别true标记为监听中
startListening(防重入)cancel旧会话false

true

先false再true
stop停止识别false标记为未监听
cancel取消识别false标记为未监听
onResult(isLast=true)收到最终结果false识别自然结束
onComplete会话完成false兜底处理
onError发生错误false错误恢复

4.2

状态转换图

startListening

false

└────────────────────────────────────┘

4.3

防重入的实现

startListening中:

if(this.isListening){this.asrEngine.cancel(this.sessionId);this.isListening=false;}

这段代码确保了同一时间只有一个活跃的识别会话

如果用户快速连续点击"开始"按钮,不会出现多个会话冲突。

用户快速点击两次"开始":

第1次点击:

第2次点击(第1次还在识别中):

isListening

竞态条件分析

有一个潜在的竞态条件:stop方法将isListening设为false,但onResult(isLast=true)回调可能在之后触发,也会将isListening设为false

//

stop方法this.asrEngine.finish(this.sessionId);this.isListening=false;//

稍后,onResult回调触发onResult(sessionId,result){if(result.isLast){plugin.isListening=false;//

第2次设false(重复但无害)}}

这种重复设置是无害的——false设两次还是false

flutter_speech的设计选择了"宁可重复也不遗漏"的策略。

五、边界场景处理:重复调用、引擎未初始化

5.1

边界场景清单

场景stop的行为cancel的行为
正常识别中finish

+

不返回结果

未在识别(isListening=false)跳过finish,返回success跳过cancel,返回success
引擎未初始化(asrEngine=null)跳过,返回success跳过,返回success
连续调用两次stop第1次正常,第2次跳过第1次正常,第2次跳过
stop后立即cancelstop正常,cancel跳过-
cancel后立即stopcancel正常,stop跳过-

5.2

引擎未初始化

if(this.asrEngine&&this.isListening){//

只有引擎存在且正在监听时才执行}result.success(true);//

无论如何都返回success

如果用户在没有调用activate的情况下直接调用stopcancelasrEngine为null,条件不满足,直接返回success。

不会报错,也不会崩溃。

5.3

isListening

返回success

第二次调用时,isListening已经是false,不会重复调用finish

这是安全的。

5.4

stop和cancel交叉调用

stop()

finish

返回success

先stop后cancel,cancel会被跳过。

反过来也一样。

这是正确的行为——已经停止了就不需要再取消。

六、stop/cancel

Dart

Dart层的调用

//

停止识别Futurestop()=>_channel.invokeMethod("speech.stop");//

取消识别Futurecancel()=>_channel.invokeMethod("speech.cancel");

6.2

示例App中的使用

//

停止按钮ElevatedButton(onPressed:_isListening?()=>_speech.stop():null,child:Text('Stop'),),//

取消按钮ElevatedButton(onPressed:_isListening?()=>_speech.cancel():null,child:Text('Cancel'),),

注意按钮的onPressed只在_isListeningtrue时才可点击。

这是UI层的防重入——如果没在识别,按钮是灰色的。

6.3

stop后的结果获取

stop后,Dart层通过recognitionCompleteHandler回调获取最终结果:

_speech.setRecognitionCompleteHandler((Stringtext){setState((){_transcription=text;_isListening=false;});});

cancel后,recognitionCompleteHandler不会被调用,所以_transcription保持之前的值(部分结果或空字符串)。

6.4

完整的交互时序

stop场景

Dart:

_speech.stop()

onMethodCall("speech.stop")

Native:

channel.invokeMethod('speech.onSpeech',

text)

channel.invokeMethod('speech.onRecognitionComplete',

text)

recognitionCompleteHandler(text)

cancel场景

Dart:

_speech.cancel()

onMethodCall("speech.cancel")

Native:

(不会有后续回调)

七、与Android实现的对比

7.1

stop:

privatevoidstopListening(MethodChannel.Resultresult){try{if(speechRecognizer!=null&&isListening){speechRecognizer.stopListening();isListening=false;}result.success(true);}catch(Exceptione){result.success(true);}}

OpenHarmony

stop

privatestop(result:MethodResult):void{try{if(this.asrEngine&&this.isListening){this.asrEngine.finish(this.sessionId);this.isListening=false;}result.success(true);}catch(e){console.error(TAG,`stop

error:${JSON.stringify(e)}`);result.success(true);}}

7.2

差异点

差异AndroidOpenHarmony
API名称stopListening()finish(sessionId)
需要sessionId

不需要

需要

错误日志有console.error
代码结构几乎一样几乎一样

两个平台的实现高度相似,这说明flutter_speech的适配做得很好——保持了跨平台的一致性。

八、最佳实践与注意事项

8.1

何时用stop,何时用cancel

场景推荐操作原因
用户说完了,想要结果stop需要最终识别结果
用户想重新说cancel不需要当前结果
切换语言cancel旧语言的结果没用
页面退出cancel不需要结果了
超时处理stop尽量保留已识别的内容
错误恢复cancel清理状态重新开始

8.2

注意事项

  1. 不要在stop后立即startListening:finish是异步的,需要等onComplete回调后再开始新的识别
  2. cancel后可以立即startListening:cancel是立即生效的
  3. 不要忘记更新UI状态:stop/cancel后要更新按钮状态
  4. destroyEngine前先stop/cancel:确保识别已停止再销毁引擎
//

正确的销毁顺序if(this.isListening){this.asrEngine.cancel(this.sessionId);//

先停止}this.asrEngine.shutdown();//

再销毁

总结

本文详细讲解了flutter_speech中语音识别的停止与取消:

  1. 语义区别:stop(finish)返回最终结果,cancel丢弃结果
  2. 实现结构:两个方法代码几乎一样,只是调用的API不同
  3. 状态管理:通过isListening标志防止重复操作和竞态条件
  4. 边界处理:引擎未初始化、未在监听、重复调用都能安全处理
  5. 错误策略:异常时也返回success,避免不必要的错误提示

下一篇我们讲引擎销毁与资源释放——destroyEngine方法的实现和资源管理的最佳实践。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!

/>

相关资源:

  • Core

    Speech

    finish/cancel文档

  • Android

    SpeechRecognizer.stopListening

  • Android

    SpeechRecognizer.cancel

  • flutter_speech

    OpenHarmony源码

  • 状态机设计模式
  • Flutter

    Platform

    Channel通信

  • 开源鸿蒙跨平台社区
  • ArkTS异步编程指南



SEO优化服务概述

作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。

百度官方合作伙伴 白帽SEO技术 数据驱动优化 效果长期稳定

SEO优化核心服务

网站技术SEO

  • 网站结构优化 - 提升网站爬虫可访问性
  • 页面速度优化 - 缩短加载时间,提高用户体验
  • 移动端适配 - 确保移动设备友好性
  • HTTPS安全协议 - 提升网站安全性与信任度
  • 结构化数据标记 - 增强搜索结果显示效果

内容优化服务

  • 关键词研究与布局 - 精准定位目标关键词
  • 高质量内容创作 - 原创、专业、有价值的内容
  • Meta标签优化 - 提升点击率和相关性
  • 内容更新策略 - 保持网站内容新鲜度
  • 多媒体内容优化 - 图片、视频SEO优化

外链建设策略

  • 高质量外链获取 - 权威网站链接建设
  • 品牌提及监控 - 追踪品牌在线曝光
  • 行业目录提交 - 提升网站基础权威
  • 社交媒体整合 - 增强内容传播力
  • 链接质量分析 - 避免低质量链接风险

SEO服务方案对比

服务项目 基础套餐 标准套餐 高级定制
关键词优化数量 10-20个核心词 30-50个核心词+长尾词 80-150个全方位覆盖
内容优化 基础页面优化 全站内容优化+每月5篇原创 个性化内容策略+每月15篇原创
技术SEO 基本技术检查 全面技术优化+移动适配 深度技术重构+性能优化
外链建设 每月5-10条 每月20-30条高质量外链 每月50+条多渠道外链
数据报告 月度基础报告 双周详细报告+分析 每周深度报告+策略调整
效果保障 3-6个月见效 2-4个月见效 1-3个月快速见效

SEO优化实施流程

我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:

1

网站诊断分析

全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。

2

关键词策略制定

基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。

3

技术优化实施

解决网站技术问题,优化网站结构,提升页面速度和移动端体验。

4

内容优化建设

创作高质量原创内容,优化现有页面,建立内容更新机制。

5

外链建设推广

获取高质量外部链接,建立品牌在线影响力,提升网站权威度。

6

数据监控调整

持续监控排名、流量和转化数据,根据效果调整优化策略。

SEO优化常见问题

SEO优化一般需要多长时间才能看到效果?
SEO是一个渐进的过程,通常需要3-6个月才能看到明显效果。具体时间取决于网站现状、竞争程度和优化强度。我们的标准套餐一般在2-4个月内开始显现效果,高级定制方案可能在1-3个月内就能看到初步成果。
你们使用白帽SEO技术还是黑帽技术?
我们始终坚持使用白帽SEO技术,遵循搜索引擎的官方指南。我们的优化策略注重长期效果和可持续性,绝不使用任何可能导致网站被惩罚的违规手段。作为百度官方合作伙伴,我们承诺提供安全、合规的SEO服务。
SEO优化后效果能持续多久?
通过我们的白帽SEO策略获得的排名和流量具有长期稳定性。一旦网站达到理想排名,只需适当的维护和更新,效果可以持续数年。我们提供优化后维护服务,确保您的网站长期保持竞争优势。
你们提供SEO优化效果保障吗?
我们提供基于数据的SEO效果承诺。根据服务套餐不同,我们承诺在约定时间内将核心关键词优化到指定排名位置,或实现约定的自然流量增长目标。所有承诺都会在服务合同中明确约定,并提供详细的KPI衡量标准。

SEO优化效果数据

基于我们服务的客户数据统计,平均优化效果如下:

+85%
自然搜索流量提升
+120%
关键词排名数量
+60%
网站转化率提升
3-6月
平均见效周期

行业案例 - 制造业

  • 优化前:日均自然流量120,核心词无排名
  • 优化6个月后:日均自然流量950,15个核心词首页排名
  • 效果提升:流量增长692%,询盘量增加320%

行业案例 - 电商

  • 优化前:月均自然订单50单,转化率1.2%
  • 优化4个月后:月均自然订单210单,转化率2.8%
  • 效果提升:订单增长320%,转化率提升133%

行业案例 - 教育

  • 优化前:月均咨询量35个,主要依赖付费广告
  • 优化5个月后:月均咨询量180个,自然流量占比65%
  • 效果提升:咨询量增长414%,营销成本降低57%

为什么选择我们的SEO服务

专业团队

  • 10年以上SEO经验专家带队
  • 百度、Google认证工程师
  • 内容创作、技术开发、数据分析多领域团队
  • 持续培训保持技术领先

数据驱动

  • 自主研发SEO分析工具
  • 实时排名监控系统
  • 竞争对手深度分析
  • 效果可视化报告

透明合作

  • 清晰的服务内容和价格
  • 定期进展汇报和沟通
  • 效果数据实时可查
  • 灵活的合同条款

我们的SEO服务理念

我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。

提交需求或反馈

Demand feedback