前言

欢迎加入开源鸿蒙跨平台社区: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适配的学习曲线不陡。主要的额外工作在于:

  1. 学习ArkTS语法(和TypeScript很像)
  2. 理解Ability模型(和Activity类似但不完全一样)
  3. 处理语言限制(Core Speech Kit的限制)
  4. 配置文件的编写(比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实现做了全面的横向对比:

  1. 引擎架构:OHOS异步创建、语言前置指定,与Android同步创建不同
  2. 权限机制:OHOS的async/await比Android的回调分离更优雅
  3. 回调模型:OHOS用isLast字段合并了部分结果和最终结果
  4. 语言支持:OHOS仅中文,是最大的差异点
  5. 开发语言:ArkTS和Java相似度高,适配学习成本低
  6. 核心流程:三个平台完全一致,这是跨平台抽象的基础

至此,第1-20篇全部完成。我们从项目背景讲到了跨平台对比,覆盖了flutter_speech OpenHarmony适配的所有核心知识。从第21篇开始,我们将进入进阶和扩展阶段——错误处理、调试技巧、性能优化、测试、部署等话题。

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


相关资源:

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐