Flutter三方库适配OpenHarmony【flutter_speech】— 跨平台差异对比
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net这是本系列前20篇的收官之作。前面我们分别分析了Android、iOS、OpenHarmony三个平台的实现,今天把它们放在一起做一次全面的横向对比。做跨平台开发最有价值的事情之一,就是理解不同平台之间的共性和差异。共性让我们能用统一的抽象(Dart API)屏蔽底层差异;差异则决定了适配
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
这是本系列前20篇的收官之作。前面我们分别分析了Android、iOS、OpenHarmony三个平台的实现,今天把它们放在一起做一次全面的横向对比。
做跨平台开发最有价值的事情之一,就是理解不同平台之间的共性和差异。共性让我们能用统一的抽象(Dart API)屏蔽底层差异;差异则决定了适配的工作量和技术方案。
我在适配flutter_speech的过程中,最深的感受是:OpenHarmony和Android的相似度远高于和iOS的相似度。如果你之前做过Android的Flutter插件开发,转到OpenHarmony的学习成本其实不高。但也有一些OpenHarmony独有的特性(比如异步引擎创建、ArkTS语法),需要额外注意。
今天我们用大量的对比表格和代码示例,把三个平台的差异一次讲透。
💡 阅读建议:本文表格较多,建议收藏后慢慢看。每张表格都是我花时间整理的,对跨平台适配有很高的参考价值。
一、Android SpeechRecognizer vs OHOS Core Speech Kit
1.1 引擎架构对比
| 维度 | Android SpeechRecognizer | OHOS Core Speech Kit |
|---|---|---|
| 所属框架 | android.speech | @kit.CoreSpeechKit |
| 引擎类 | SpeechRecognizer | SpeechRecognitionEngine |
| 创建方式 | SpeechRecognizer.createSpeechRecognizer(context) | speechRecognizer.createEngine(params) |
| 创建模式 | 同步 | 异步(Promise) |
| 语言指定时机 | startListening时(Intent) | createEngine时 |
| 在线/离线 | 系统自动选择 | createEngine时指定 |
| 音频管理 | 系统自动 | 系统自动(参数可配) |
| 销毁方法 | destroy() | shutdown() |
1.2 引擎创建代码对比
// Android - 同步创建,语言在listen时指定
SpeechRecognizer recognizer = SpeechRecognizer.createSpeechRecognizer(activity);
recognizer.setRecognitionListener(listener);
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "zh_CN");
recognizer.startListening(intent);
// OpenHarmony - 异步创建,语言在创建时指定
const engine = await speechRecognizer.createEngine({
language: "zh-CN",
online: 1
});
engine.setListener(listener);
engine.startListening(params);
📌 关键差异:Android的语言是在startListening时通过Intent指定的,可以每次识别用不同的语言。OpenHarmony的语言在createEngine时就确定了,要换语言必须重新创建引擎。
1.3 能力检测对比
// Android
boolean available = SpeechRecognizer.isRecognitionAvailable(context);
// OpenHarmony
const available = canIUse('SystemCapability.AI.SpeechRecognizer');
两者都是一行代码,但检测的粒度不同:
- Android检测的是"设备上是否安装了语音识别服务"
- OpenHarmony检测的是"设备是否具备语音识别的系统能力"
1.4 语言支持对比

这是目前最大的差距。但从实际使用角度看,国内用户主要使用中文,这个限制的影响没有数字看起来那么大。
二、权限申请机制对比:ActivityCompat vs abilityAccessCtrl
2.1 权限声明对比
<!-- Android: AndroidManifest.xml -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
// OpenHarmony: module.json5 (宿主App中)
{
"module": {
"requestPermissions": [{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:microphone_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}]
}
}
| 对比项 | Android | OpenHarmony |
|---|---|---|
| 声明文件 | AndroidManifest.xml | module.json5 |
| 声明格式 | XML | JSON5 |
| 权限名 | android.permission.RECORD_AUDIO | ohos.permission.MICROPHONE |
| 需要reason | ❌ 不需要 | ✅ 必须提供 |
| 需要usedScene | ❌ 不需要 | ✅ 必须提供 |
OpenHarmony的权限声明更严格——必须提供使用原因和使用场景。这对用户隐私保护更好,但开发者需要多写一些配置。
2.2 动态申请对比
// Android - 回调模式
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE);
// 结果在另一个方法中获取
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限已授予
}
}
// OpenHarmony - async/await模式
const atManager = abilityAccessCtrl.createAtManager();
const grantResult = await atManager.requestPermissionsFromUser(
this.abilityContext,
['ohos.permission.MICROPHONE']
);
const granted = grantResult.authResults[0] ===
abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
| 对比项 | Android | OpenHarmony |
|---|---|---|
| 异步模式 | 回调分离 | async/await |
| 代码分布 | 两个方法 | 一个方法内 |
| 代码行数 | ~15行 | ~6行 |
| 可读性 | 中等 | 好 |
| 需要requestCode | ✅ 需要 | ❌ 不需要 |
| 需要Context | Activity | UIAbilityContext |
💡 OpenHarmony的权限API设计更现代。async/await让权限申请的代码是线性的,不需要在两个方法之间跳转。这是我在适配过程中最喜欢的一个改进。
2.3 权限检查对比
// Android - 检查权限状态
int status = ContextCompat.checkSelfPermission(context,
Manifest.permission.RECORD_AUDIO);
boolean granted = (status == PackageManager.PERMISSION_GRANTED);
// OpenHarmony - 检查权限状态
const atManager = abilityAccessCtrl.createAtManager();
const tokenId = this.abilityContext.applicationInfo.accessTokenId;
const status = atManager.checkAccessTokenSync(tokenId,
'ohos.permission.MICROPHONE');
const granted = (status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED);
OpenHarmony的检查多了一个accessTokenId的概念,这是OpenHarmony安全模型的一部分——每个应用都有一个唯一的访问令牌。
三、回调模型对比:RecognitionListener vs setListener
3.1 接口定义对比
// Android RecognitionListener - 8个回调方法
public interface RecognitionListener {
void onReadyForSpeech(Bundle params);
void onBeginningOfSpeech();
void onRmsChanged(float rmsdB);
void onBufferReceived(byte[] buffer);
void onEndOfSpeech();
void onError(int error);
void onResults(Bundle results);
void onPartialResults(Bundle partialResults);
}
// OpenHarmony SpeechRecognitionListener - 5个回调方法
interface SpeechRecognitionListener {
onStart(sessionId: string, eventMessage: string): void;
onEvent(sessionId: string, eventCode: number, eventMessage: string): void;
onResult(sessionId: string, result: SpeechRecognitionResult): void;
onComplete(sessionId: string, eventMessage: string): void;
onError(sessionId: string, errorCode: number, errorMessage: string): void;
}
3.2 回调映射
| Android回调 | OpenHarmony回调 | Dart事件 | 说明 |
|---|---|---|---|
| onReadyForSpeech | onStart | speech.onRecognitionStarted | 准备就绪 |
| onBeginningOfSpeech | onEvent | (不转发) | 检测到语音 |
| onRmsChanged | (无) | (不转发) | 音量变化 |
| onBufferReceived | (无) | (不转发) | 音频数据 |
| onEndOfSpeech | onComplete | speech.onRecognitionComplete | 语音结束 |
| onError | onError | speech.onError | 错误 |
| onResults | onResult(isLast=true) | speech.onRecognitionComplete | 最终结果 |
| onPartialResults | onResult(isLast=false) | speech.onSpeech | 部分结果 |
3.3 结果获取方式对比
// Android - 从Bundle中提取结果列表
@Override
public void onResults(Bundle results) {
ArrayList<String> matches = results.getStringArrayList(
SpeechRecognizer.RESULTS_RECOGNITION);
if (matches != null && !matches.isEmpty()) {
String text = matches.get(0); // 取第一个结果
}
}
// OpenHarmony - 直接从result对象获取
onResult(sessionId: string, result: speechRecognizer.SpeechRecognitionResult): void {
const text = result.result; // 直接获取文本
const isLast = result.isLast; // 是否是最终结果
}
OpenHarmony的结果获取更直观——直接访问result.result属性,不需要从Bundle中解包。
3.4 部分结果与最终结果的处理差异
// Android - 两个独立的回调
@Override
public void onPartialResults(Bundle partialResults) {
// 部分结果
String text = partialResults.getStringArrayList(...).get(0);
channel.invokeMethod("speech.onSpeech", text);
}
@Override
public void onResults(Bundle results) {
// 最终结果
String text = results.getStringArrayList(...).get(0);
channel.invokeMethod("speech.onRecognitionComplete", text);
}
// OpenHarmony - 一个回调,通过isLast区分
onResult(sessionId, result) {
channel?.invokeMethod('speech.onSpeech', result.result); // 每次都发
if (result.isLast) {
channel?.invokeMethod('speech.onRecognitionComplete', result.result); // 最终时额外发
}
}
| 设计方式 | Android | OpenHarmony |
|---|---|---|
| 回调数量 | 2个(onPartialResults + onResults) | 1个(onResult) |
| 区分方式 | 不同的回调方法 | isLast字段 |
| 代码复杂度 | 分散在两个方法 | 集中在一个方法 |
四、语言支持范围差异
4.1 详细对比
| 维度 | Android | iOS | OpenHarmony |
|---|---|---|---|
| 支持语言数 | 100+ | 60+ | 1 |
| 中文 | ✅ | ✅ | ✅ |
| 英文 | ✅ | ✅ | ❌ |
| 中英混合 | ✅ | ✅ | ⚠️ 部分支持 |
| 离线中文 | 部分设备 | iOS 13+ | ✅ |
| 方言支持 | 部分 | 部分 | ❌ |
| 语言切换 | 运行时切换 | 运行时切换 | 需重建引擎 |
4.2 flutter_speech的应对策略
| 差异 | 应对策略 | 实现位置 |
|---|---|---|
| 语言数量少 | isSupportedLocale校验 | Native端 |
| 不支持的语言 | 返回ERROR_LANGUAGE_NOT_SUPPORTED | Native端 |
| 中英混合 | 不做特殊处理,依赖引擎能力 | - |
| 语言切换需重建 | 每次start都重新activate | Dart端 |
| UI语言限制 | Platform.isOhos判断 | Dart端 |
4.3 locale格式差异
| 平台 | 格式 | 示例 | 转换需求 |
|---|---|---|---|
| Dart | 下划线 | zh_CN | 源格式 |
| Android | 下划线 | zh_CN | 无需转换 |
| iOS | 连字符 | zh-CN | 需要转换 |
| OpenHarmony | 连字符 | zh-CN | 需要转换 |
五、部分识别结果(Partial Results)的处理差异
5.1 三平台的部分结果机制
Android:
// 需要显式开启部分结果
intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true);
// 通过独立的回调接收
@Override
public void onPartialResults(Bundle partialResults) { ... }
iOS:
// 默认支持部分结果
// 通过recognitionTask的resultHandler接收
[recognizer recognitionTaskWithRequest:request
resultHandler:^(SFSpeechRecognitionResult *result, NSError *error) {
if (!result.isFinal) {
// 部分结果
} else {
// 最终结果
}
}];
OpenHarmony:
// 默认支持部分结果
// 通过onResult回调接收
onResult(sessionId, result) {
if (!result.isLast) {
// 部分结果
} else {
// 最终结果
}
}
5.2 对比总结
| 维度 | Android | iOS | OpenHarmony |
|---|---|---|---|
| 默认开启 | ❌ 需要显式开启 | ✅ 默认开启 | ✅ 默认开启 |
| 区分字段 | 不同回调方法 | isFinal | isLast |
| 结果格式 | ArrayList<String> | SFTranscription | SpeechRecognitionResult |
| 多候选 | ✅ 支持 | ✅ 支持 | ❌ 不支持 |
| 置信度 | ✅ 有 | ✅ 有 | ❌ 无 |
六、插件接口对比
6.1 插件类声明
// Android
public class FlutterSpeechRecognitionPlugin
implements FlutterPlugin, MethodCallHandler, ActivityAware { }
// iOS
@interface FlutterSpeechRecognitionPlugin : NSObject<FlutterPlugin>
@end
// OpenHarmony
export default class FlutterSpeechPlugin
implements FlutterPlugin, MethodCallHandler, AbilityAware { }
6.2 接口实现对比
| 接口 | Android | iOS | OpenHarmony |
|---|---|---|---|
| 插件接口 | FlutterPlugin | FlutterPlugin(协议) | FlutterPlugin |
| 方法处理 | MethodCallHandler | handleMethodCall | MethodCallHandler |
| 上下文感知 | ActivityAware | (通过registrar) | AbilityAware |
| 唯一标识 | (无) | (无) | getUniqueClassName() |
6.3 生命周期方法对比
| 生命周期 | Android | OpenHarmony | 说明 |
|---|---|---|---|
| 引擎绑定 | onAttachedToEngine | onAttachedToEngine | 完全一致 |
| 引擎解绑 | onDetachedFromEngine | onDetachedFromEngine | 完全一致 |
| 上下文绑定 | onAttachedToActivity | onAttachedToAbility | 名称不同 |
| 上下文解绑 | onDetachedFromActivity | onDetachedFromAbility | 名称不同 |
| 配置变更 | onReattachedToActivityForConfigChanges | (无) | OHOS不需要 |
| 配置变更 | onDetachedFromActivityForConfigChanges | (无) | OHOS不需要 |
📌 OpenHarmony少了两个配置变更相关的方法。这是因为OpenHarmony的Ability模型不像Android的Activity那样在配置变更(如屏幕旋转)时需要重建。
七、开发语言与语法对比
7.1 语言特性对比
| 特性 | Java (Android) | ObjC (iOS) | ArkTS (OpenHarmony) |
|---|---|---|---|
| 类型系统 | 静态强类型 | 动态+静态 | 静态强类型 |
| 空安全 | @Nullable注解 | nullable指针 | Type | null |
| 异步 | 回调/Future | Block回调 | async/await |
| 字符串模板 | "text " + var | [NSString stringWithFormat:] | `text ${var}` |
| 异常处理 | try-catch | @try-@catch / NSError | try-catch |
| 接口 | interface | @protocol | interface |
| 导出 | public class | @interface | export default class |
7.2 同一功能的三种写法
以"停止识别"为例:
// Android - Java
private void stopListening(MethodChannel.Result result) {
try {
if (speechRecognizer != null && isListening) {
speechRecognizer.stopListening();
isListening = false;
}
result.success(true);
} catch (Exception e) {
result.success(true);
}
}
// iOS - Objective-C
- (void)stopRecognition:(FlutterResult)result {
if (self.audioEngine.isRunning) {
[self.audioEngine stop];
[self.recognitionRequest endAudio];
self.isListening = NO;
}
result(@YES);
}
// OpenHarmony - ArkTS
private stop(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);
}
}
ArkTS和Java的相似度最高——都是try-catch结构,都有条件检查,都返回success。iOS因为需要手动管理AVAudioEngine,代码结构有所不同。
八、适配工作量评估
8.1 各平台代码量对比
| 平台 | 核心文件 | 代码行数 | 配置文件数 |
|---|---|---|---|
| Android | FlutterSpeechRecognitionPlugin.java | ~200行 | 2 (build.gradle, AndroidManifest.xml) |
| iOS | FlutterSpeechRecognitionPlugin.m | ~250行 | 1 (podspec) |
| OpenHarmony | FlutterSpeechPlugin.ets | 265行 | 5 (oh-package.json5, build-profile.json5, module.json5, index.ets, hvigorfile.ts) |
OpenHarmony的核心代码量和其他平台相当,但配置文件更多。这是OpenHarmony模块化设计的特点——每个配置文件职责明确。
8.2 适配难度评估
| 维度 | 难度 | 说明 |
|---|---|---|
| 工程创建 | ⭐⭐ | 目录结构和配置文件较多,但有模板可参考 |
| 接口实现 | ⭐⭐ | 和Android高度相似,照着改语法即可 |
| 权限管理 | ⭐⭐ | async/await比Android的回调更简单 |
| 引擎创建 | ⭐⭐⭐ | 异步创建需要注意时序 |
| 监听器 | ⭐⭐ | 回调结构清晰,映射关系明确 |
| 语言处理 | ⭐⭐⭐ | 需要处理语言限制和格式转换 |
| 资源释放 | ⭐⭐ | 和Android类似 |
| 调试 | ⭐⭐⭐ | DevEco Studio的调试工具还在完善中 |
总体评估:如果你有Android Flutter插件开发经验,OpenHarmony适配的学习曲线不陡。主要的额外工作在于:
- 学习ArkTS语法(和TypeScript很像)
- 理解Ability模型(和Activity类似但不完全一样)
- 处理语言限制(Core Speech Kit的限制)
- 配置文件的编写(比Android多几个)
8.3 从Android适配到OpenHarmony的检查清单
- 创建ohos目录结构和配置文件
- 将Java代码转写为ArkTS
- 将ActivityAware改为AbilityAware
- 将SpeechRecognizer API替换为Core Speech Kit API
- 处理异步引擎创建(sync → async)
- 添加语言校验逻辑
- 添加locale格式转换
- 更新pubspec.yaml添加ohos平台
- 更新示例App添加平台判断
- 在真机上测试完整流程
九、总结与展望
9.1 三平台核心差异总结
| 维度 | 最大差异点 | 影响程度 |
|---|---|---|
| 语言支持 | OHOS仅中文 | ⭐⭐⭐⭐⭐ |
| 引擎创建 | OHOS是异步的 | ⭐⭐⭐ |
| 回调模型 | iOS用Block,其他用Listener | ⭐⭐ |
| 权限申请 | iOS需要两个权限 | ⭐⭐ |
| 音频管理 | iOS需要手动管理AVAudioEngine | ⭐⭐⭐ |
| 配置文件 | OHOS配置文件最多 | ⭐⭐ |
| 开发语言 | 三种不同语言 | ⭐⭐⭐ |
9.2 共性总结
尽管有这么多差异,三个平台的核心流程完全一致:
权限申请 → 引擎创建 → 设置监听 → 开始识别 → 接收结果 → 停止/取消 → 释放资源
这个流程就是flutter_speech Dart API的设计基础。不管底层平台怎么变,上层的Dart代码都是:
_speech.activate('zh_CN');
_speech.listen();
// 回调接收结果
_speech.stop();
这就是Flutter Plugin架构的价值——用统一的API屏蔽平台差异,让应用开发者不需要关心底层实现。
9.3 OpenHarmony的未来展望
| 当前限制 | 预期改进 | 影响 |
|---|---|---|
| 仅支持中文 | 多语言支持 | 消除最大差异 |
| 仅在线识别效果好 | 离线模型优化 | 提升无网络体验 |
| 无置信度信息 | 添加confidence字段 | 支持结果质量判断 |
| 无多候选结果 | 支持N-best | 提升灵活性 |
| 文档不够完善 | 持续完善 | 降低开发门槛 |
随着OpenHarmony生态的发展,这些差异会逐步缩小。flutter_speech的适配框架已经搭好,未来的改进主要是跟进Core Speech Kit的新能力。
总结
本文对Android、iOS、OpenHarmony三个平台的flutter_speech实现做了全面的横向对比:
- 引擎架构:OHOS异步创建、语言前置指定,与Android同步创建不同
- 权限机制:OHOS的async/await比Android的回调分离更优雅
- 回调模型:OHOS用isLast字段合并了部分结果和最终结果
- 语言支持:OHOS仅中文,是最大的差异点
- 开发语言:ArkTS和Java相似度高,适配学习成本低
- 核心流程:三个平台完全一致,这是跨平台抽象的基础
至此,第1-20篇全部完成。我们从项目背景讲到了跨平台对比,覆盖了flutter_speech OpenHarmony适配的所有核心知识。从第21篇开始,我们将进入进阶和扩展阶段——错误处理、调试技巧、性能优化、测试、部署等话题。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
更多推荐


所有评论(0)