鸿蒙多种能力并存时,目录、命名和通道协议该怎么统一
适合谁看项目里已经接了多个鸿蒙能力的人想给团队补统一约定的人不想让平台层越来越乱的人问题背景多能力项目最容易出现的混乱混乱示例后果文件命名风格不一致有的叫service有的叫bridge找文件困难通道名没有统一前缀com.foodvoyage.xxxvscom.xxx.yyy调试混乱参数有的传 Map有的传字符串不同能力参数格式不同维护困难页面层直接和原生细节纠缠页面知道原生 API 细节耦合度高项目中的真实场景食界探味当前的 4 个能力已经形成了一套模式能力Flutter 文件ArkTS 文件channel 名语音识别speech_recognition_channel.dartSpeechRecognitionPlugin.etscom.foodvoyage.speech_recognitionTTStext_to_speech_channel.dartTextToSpeechPlugin.etscom.foodvoyage.text_to_speechIntent 导航intent_navigation_channel.dartIntentNavigationPlugin.etscom.foodvoyage.intent_navigation防窥保护anti_peep_protection_channel.dartAntiPeepProtectionPlugin.etscom.foodvoyage.anti_peep_protection核心实现一、目录先按平台边界层集中Flutter 侧app/lib/core/platform/ ├── speech_recognition_channel.dart ├── text_to_speech_channel.dart ├── intent_navigation_channel.dart └── anti_peep_protection_channel.dart鸿蒙侧app/ohos/entry/src/main/ets/plugins/ ├── SpeechRecognitionPlugin.ets ├── TextToSpeechPlugin.ets ├── IntentNavigationPlugin.ets └── AntiPeepProtectionPlugin.ets为什么这样组织好处说明新同事一眼知道去哪找平台能力都在core/platform/排错时快速缩小范围先看目录就知道涉及哪些能力新增能力不用重新发明规则照着现有文件加一个就行Flutter 和鸿蒙两边对应关系清楚文件名一一对应不要这样做❌ 把能力分散到各个页面目录 features/search/speech_service.dart features/ai_assistant/tts_bridge.dart features/dish_detail/navigation_helper.dart ✅ 统一收在平台边界层 core/platform/speech_recognition_channel.dart core/platform/text_to_speech_channel.dart二、命名先按能力本身统一当前项目已经形成了很稳的命名模式层命名模式示例Flutter 侧xxx_channel.dartspeech_recognition_channel.dartArkTS 侧XxxPlugin.etsSpeechRecognitionPlugin.etschannel 名com.foodvoyage.xxxcom.foodvoyage.speech_recognition方法名动词 名词startListening、stopListening命名规范表格规范说明示例Flutter 文件snake_case_channel.darttext_to_speech_channel.dartArkTS 文件PascalCasePlugin.etsTextToSpeechPlugin.etschannel 名com.foodvoyage.snake_casecom.foodvoyage.text_to_speech类名PascalCaseChannel/PascalCasePluginTextToSpeechChannel/TextToSpeechPlugin方法名camelCasestartListening、speak、stop如果命名不统一会怎样❌ 命名混乱 speech_service.dart / TtsBridge.ets / com.xxx.tts → 找文件困难channel 名对不上 ✅ 命名统一 text_to_speech_channel.dart / TextToSpeechPlugin.ets / com.foodvoyage.text_to_speech → 一看就知道对应关系三、通道协议要先统一什么是命令什么是事件当前项目的 4 个能力分属 3 种通道模型通道模型特点示例命令型发起一次命令等待一次结果语音识别、TTS事件型开启后持续接收状态事件防窥保护混合型原生主动推送 Flutter 主动消费Intent 导航命令型通道模式// Flutter 侧 static FutureString startListening() async { return await _channel.invokeMethodString(startListening); } static Futurevoid stop() async { await _channel.invokeMethodvoid(stop); }// ArkTS 侧 onMethodCall(call: MethodCall, result: MethodResult): void { switch (call.method) { case startListening: this.handleStart(result); break; case stopListening: this.handleStop(result); break; } }事件型通道模式// Flutter 侧 static void initialize() { _channel.setMethodCallHandler((call) async { if (call.method onEvent) { final event call.arguments[event] as String?; state.value event ACTIVE ? EventState.active : EventState.idle; } }); } static Futurevoid activate() async { await _channel.invokeMethodvoid(activate); }// ArkTS 侧 private onStatusChange(status: string): void { this.channel?.invokeMethod(onEvent, { event: status }); }混合型通道模式// Flutter 侧 static void init(GoRouter router) { _channel.setMethodCallHandler((call) async { if (call.method onIntentNavigation) { // 实时推送 } }); _consumePending(); // 主动消费 pending }统一协议不是要求所有能力都只剩一个invokeMethod而是要求同类能力用相近语义不同类能力的差异是有意识的四、参数风格也应该尽量统一命令型参数结构化 Map// 语音识别 await _channel.invokeMethod(startListening, {language: zh-CN}); // TTS await _channel.invokeMethod(speak, {text: 你好}); // Intent 导航 await _channel.invokeMethod(navigateToPage, {pageId: search, dishId: xxx});事件型参数固定字段// 防窥事件 { event: HIDE, // 事件名 detail: anti-peek // 事件详情 }错误码统一格式// 成功 result.success(null); // 参数错误 result.error(INVALID_ARGUMENT, 参数为空, null); // 业务错误 result.error(TTS_ERROR, 播报失败, null); // 权限错误 result.error(PERMISSION_DENIED, 权限被拒绝, null);参数风格统一的好处好处说明页面层消费方式一致不需要为每个能力写不同的解析逻辑调试时日志格式一致更容易定位问题新增能力时有模板照着现有模式写就行错误处理可以复用统一的错误码可以统一处理关键代码位置文件作用app/lib/core/platform/Flutter 通道层统一目录app/ohos/entry/src/main/ets/plugins/鸿蒙插件层统一目录app/ohos/entry/src/main/ets/entryability/EntryAbility.ets插件注册统一入口统一规则总结目录统一 ├─ Flutterapp/lib/core/platform/ └─ 鸿蒙app/ohos/entry/src/main/ets/plugins/ 命名统一 ├─ Flutter 文件xxx_channel.dart ├─ ArkTS 文件XxxPlugin.ets └─ channel 名com.foodvoyage.xxx 通道模型统一 ├─ 命令型invokeMethod → 等待返回 ├─ 事件型invokeMethod 启动 onEvent 回推 └─ 混合型invokeMethod pending 消费 参数风格统一 ├─ 命令型结构化 Map ├─ 事件型{ event, detail } └─ 错误{ code, message }常见坑每加一种能力就重新发明一套命名— 应该照着现有模式文件既有service、又有bridge、又有channel— 统一用channel通道协议没有统一事件和错误字段— 事件用{ event, detail }错误用{ code, message }参数风格完全靠临时习惯决定— 应该有统一规范Flutter 和 ArkTS 两边各自命名一套— 文件名应该一一对应新能力一来就复制旧代码但没有同步复制命名和字段规范— 复制时也要复制规范可复用模板新增能力命名模板新增能力[能力名] Flutter 文件app/lib/core/platform/[能力名]_channel.dart ArkTS 文件app/ohos/entry/src/main/ets/plugins/[能力名]Plugin.ets channel 名com.foodvoyage.[能力名] 类名 Flutter[能力名]Channel ArkTS[能力名]Plugin通道协议模板命令型 Flutter: static FutureT method(MapString, dynamic args) ArkTS: onMethodCall → result.success(result) 事件型 Flutter: initialize() setMethodCallHandler ArkTS: systemApi.on → channel.invokeMethod(onEvent, { event, detail }) 混合型 Flutter: init() consumePending ArkTS: navigateToPage() setPendingNavigation()参数风格模板命令型参数{ key: value } 事件型参数{ event: EVENT_NAME, detail: optional detail } 错误格式result.error(ERROR_CODE, 错误描述, null)新增能力检查清单目录 □ Flutter 文件在 core/platform/ 下 □ ArkTS 文件在 plugins/ 下 命名 □ Flutter 文件名是 xxx_channel.dart □ ArkTS 文件名是 XxxPlugin.ets □ channel 名是 com.foodvoyage.xxx □ 类名和文件名对应 通道模型 □ 是命令型、事件型还是混合型 □ 同类能力用相近语义 参数风格 □ 命令型传结构化 Map □ 事件型用 { event, detail } □ 错误用 { code, message }本篇总结多能力并存之后工程约定本身就会变成开发效率问题。目录、命名和协议越早统一后面继续扩展越不容易乱目录统一— Flutter 在core/platform/鸿蒙在plugins/命名统一—xxx_channel.dart/XxxPlugin.ets/com.foodvoyage.xxx通道模型统一— 命令型 / 事件型 / 混合型同类能力用相近语义参数风格统一— 结构化 Map、固定事件字段、统一错误格式当前项目已经有一套不错的雏形后面最重要的是继续保持一致。