适合谁看
-
正在设计平台通道结构的人
-
不确定该做一个 channel 还是多个 channel 的人
-
想用真实项目验证拆分方式的人
问题背景
平台层最容易在"统一"名义下越做越重。
很多项目一开始平台能力还不多时,会很自然地想:
-
反正都是原生能力
-
反正都通过
MethodChannel -
那不如做成一个统一 channel
这个想法的诱惑很强,因为它看上去:
-
文件更少
-
入口更统一
-
像是提前做了抽象
但真实项目往往不是这样演化的。
平台能力一旦变多,真正重要的问题就不再是"入口是不是只有一个",而是:
能力边界到底有没有被保护住。
项目中的真实场景
当前这个鸿蒙 Flutter 项目的通信设计非常明确:
-
com.foodvoyage.speech_recognition -
com.foodvoyage.text_to_speech -
com.foodvoyage.intent_navigation -
com.foodvoyage.anti_peep_protection
Flutter 侧对应的是:
-
speech_recognition_channel.dart -
text_to_speech_channel.dart -
intent_navigation_channel.dart -
anti_peep_protection_channel.dart
ArkTS 侧对应的是:
-
SpeechRecognitionPlugin.ets -
TextToSpeechPlugin.ets -
IntentNavigationPlugin.ets -
AntiPeepProtectionPlugin.ets
这一组映射关系非常值得注意,因为它不是"随便拆的文件",而是鸿蒙平台能力边界和 channel 边界基本一一对应。
核心实现
先说结论:
在鸿蒙 Flutter 这类需要持续接入系统能力的项目里,一项能力一个 channel,比一个超级 channel 更稳,因为它让能力语义、文件结构、排错入口和后续扩展方向都能保持一致。
一、为什么"一项能力一个 channel"更贴近真实能力边界
看当前这个鸿蒙 Flutter 项目里的四类能力就能发现,它们根本不是同一种东西:
-
语音识别是输入能力
-
TTS 是输出能力
-
Intent 是系统入口能力
-
防窥是状态与事件能力
虽然它们都需要和原生通信,但它们的:
-
方法名
-
参数结构
-
生命周期
-
返回结果
-
错误语义
都不一样。
如果这些能力被硬塞进一个 channel,最常见的结果就是:
-
同一个地方塞进大量不相干方法名
-
参数和返回值越来越不统一
-
页面层必须知道更多"这个超级平台层到底还藏了什么"
而"一项能力一个 channel"恰好能把这些不同能力拆回各自语义空间。
二、它让文件结构和通信结构天然对齐
这是当前这套鸿蒙平台通信结构一个很大的优点。
现在你看到:
-
speech_recognition_channel.dart -
SpeechRecognitionPlugin.ets
几乎不用猜就知道它们是一组。
同理:
-
text_to_speech_channel.dart -
TextToSpeechPlugin.ets
也是一组。
这种一一对应关系的价值很大,因为它让:
-
阅读结构更直接
-
新人更容易建立映射
-
排错时能快速缩小范围
如果是一个超级 channel,后面通常会变成:
-
一个超大的 Flutter 平台服务文件
-
一个超大的 ArkTS 插件分发文件
这时即使代码还没坏,理解成本也已经上来了。
三、它让页面层调用语义更清楚
页面层最怕的不是多几个 channel,而是不知道自己到底在调什么。
在当前这套鸿蒙 Flutter 结构里,页面或业务层调用时面对的是很清楚的语义:
-
我要开始识别 →
SpeechRecognitionChannel -
我要开始播报 →
TextToSpeechChannel -
我要承接系统导航 →
IntentNavigationChannel -
我要开启防窥 →
AntiPeepProtectionChannel
这比一个统一平台层里出现:
-
invoke('speech.start') -
invoke('tts.speak') -
invoke('intent.navigate')
更清晰,因为能力的归属感天然就在文件层和类层里。
四、它让不同能力可以按自己的节奏演化
这是很多人一开始不太容易想到,但后期特别重要的一点。
看当前这四类鸿蒙平台能力就知道,它们未来的扩展方向完全不同:
-
语音识别可能扩到中间结果、语言参数、连续识别
-
TTS 可能扩到播报速度、音色、队列控制
-
Intent 可能扩到更多
pageId、更多入口来源 -
防窥可能继续扩状态处理和页面联动
如果这些能力共用一个 channel 或一个超级平台服务,后面每扩一个能力,公共层都会被迫一起变复杂。
而当前这种拆法能让每类能力尽量只动自己的那一条链路。
五、从当前代码看,这种拆法为什么特别适合教程和团队维护
当前这个项目的结构还有一个很实际的好处:
- 每篇教程几乎都能围绕一条独立链路展开
比如你要讲语音识别,只需要主要看:
-
speech_recognition_channel.dart -
SpeechRecognitionPlugin.ets
要讲 Intent 导航,只需要主要看:
-
intent_navigation_channel.dart -
IntentNavigationPlugin.ets
这对于:
-
新人 onboarding
-
代码 review
-
线上排错
-
教程连载
都特别有利。
因为每个人都能先把某一条能力链学明白,而不是一开始就掉进一个超级平台层的总入口里。
六、为什么这不等于"越拆越碎越好"
说到这里,也要把边界说清楚。
我认同一项能力一个 channel,不等于我认同所有东西都无限细拆。
更准确的判断标准应该是:
- channel 边界要和能力边界一致
如果两项能力本来就是同一条生命周期、同一组语义、同一类错误模型,那它们可以收在一起。
但当前这四类鸿蒙能力显然不是这种情况。
所以当前拆法不是"为了拆而拆",而是"因为它们本来就不是一回事"。
七、超级 channel 最容易长成什么样
这也是为什么我不建议它的原因。
一个超级 channel 最常见的演化路径通常是:
-
最初觉得统一方便
-
后来开始加前缀区分能力
-
再后来方法越来越多
-
最后参数结构和返回值风格都开始漂
再往后,页面层、边界层、原生层都会开始出现一种共同症状:
-
明明都能跑
-
但没人愿意轻易改
从长期维护角度看,这是比"多几个 channel 文件"更贵的问题。
八、哪些信号说明你该拆 channel 了
如果你的平台层开始出现下面这些信号,通常就该考虑按能力拆 channel:
-
一个 channel 里方法名已经明显分属多类业务
-
参数开始大量依赖万能
Map -
页面层需要知道某些方法名前缀到底归哪一类能力
-
原生插件里的
switch (call.method)已经越来越长 -
一项能力的修改经常连带影响别的能力
这时候再继续维持"统一入口",往往只是把未来的复杂度往后拖。
关键代码位置
-
app/lib/core/platform/speech_recognition_channel.dart -
app/lib/core/platform/text_to_speech_channel.dart -
app/lib/core/platform/intent_navigation_channel.dart -
app/lib/core/platform/anti_peep_protection_channel.dart -
app/ohos/entry/src/main/ets/plugins/SpeechRecognitionPlugin.ets -
app/ohos/entry/src/main/ets/plugins/TextToSpeechPlugin.ets -
app/ohos/entry/src/main/ets/plugins/IntentNavigationPlugin.ets -
app/ohos/entry/src/main/ets/plugins/AntiPeepProtectionPlugin.ets
鸿蒙侧实现
从鸿蒙 ArkTS 侧看,一项能力一个 channel 的直接好处是:
-
每个插件类职责单一
-
方法名分发更短
-
原生事件和错误模型不容易互相污染
这对原生层长期维护非常重要。
Flutter 侧实现
从 Flutter 侧看,这种拆法让 app/lib/core/platform/ 这层也能保持清楚;从鸿蒙教程写作角度看,也更容易把每一项 HarmonyOS 能力单独讲透:
-
每个类只解决一类能力
-
页面层按业务语义调用
-
不需要理解超级平台协议
这比"统一得很漂亮,但谁都不敢动"的平台层更实用。
常见坑
-
过早统一成一个超级 channel
-
通道拆分和能力边界不一致
-
文件虽然拆开了,但底层又偷偷共享一团混杂协议
-
页面层为了省事,绕开边界层直接碰平台细节
-
明明已经该拆了,却继续把新能力塞进旧的大通道
可复用模板
一项能力
-> 一个 channel
-> 一个 Flutter 边界类
-> 一个 ArkTS 插件类
-> 一组清楚的方法名和事件名
判断标准
channel 边界 = 能力边界
不要为了统一入口而牺牲语义清晰度
本篇总结
这套鸿蒙 Flutter 项目里的一项能力一个 channel,不只是文件组织习惯,而是一种很明确的边界设计。
它让:
-
能力语义更清楚
-
文件结构更直观
-
排错入口更稳定
-
后续扩展互不拖累
对于一个还会继续接更多鸿蒙能力的真实项目来说,这种拆法通常会比"一个大而全的 channel"更稳。对于面向鸿蒙平台写教程的人来说,这种拆法也更容易把每一项系统能力讲清楚、讲完整。
