1. 为什么非要在 OpenHarmony 上搞定多语言动态注入
做 Flutter 开发的人都知道,国际化不是一个高阶需求,而是从项目第一天就必须想清楚的基建。但大多数团队在适配鸿蒙时,遇到的第一堵墙根本不是 UI 或者底层绘制,反而是像国际化这种"看起来不该有问题"的基础能力——因为生态里的主流三方库压根没有针对 OpenHarmony 的实现。
我这次要聊的 sajilo 就是典型的例子。这是一个主打极简的 Flutter 多语言库,核心思路是在运行时动态注入翻译资源,不需要重新打包 APK/HAP 就能热切语言。名字本身在尼泊尔语里就是"简单"的意思,所以它的设计哲学一直是"把复杂度藏在内部,把简单留给开发"。但再好用的库,到了鸿蒙面前都得面对一个现实:OpenHarmony 的 Flutter 分支生态并不完整,很多依赖 platform channel 的能力都需要自己动手补一层桥接。
这篇文章我会从 sajilo 的鸿蒙适配全过程入手,把多语言动态注入的机制、平台通道的桥接方式、常见的坑和排查思路都过一遍。不管你是打算给自己的项目接入类似能力,还是纯粹想研究 OpenHarmony 上 Flutter 插件怎么迁移,这篇文章都有参考价值。
先说清楚适配的价值。很多人会觉得,鸿蒙上跑 Flutter 应用,国际化功能将就一下,内置几个固定语言不就行了?如果你的应用只覆盖单一语言市场,这确实能凑合。但只要产品覆盖面稍大,比如要同时支持中、英、日、韩等语言,或者要做运营层面的动态文案下发,那么"写死"这条路立刻就走不通了。sajilo 这类库存在的意义,就是让你能在不发布新版本的前提下,让应用在运行时切换语言、注入新的文案、响应系统语言变化。这套能力在鸿蒙分支上通不了,就等于你的应用在鸿蒙生态里"半瘫痪"。
还有一个现实因素是团队成本。如果一个项目要同时维护 Flutter 侧的业务代码和鸿蒙侧的桥接代码,人力翻倍肯定是吃不消的。sajilo 的设计目标就是把"多语言动态注入"这个横切关注点收敛成一个极小的包,让上层业务代码感知不到平台差异。所以适配的核心思路就是:保留 Flutter 侧的 API 不动,在鸿蒙侧补上缺失的 platform channel 实现。只要这个桥接层补得足够干净,上层业务可以做到"零改动"平滑迁移。
下面我从头到尾拆解一遍。
2. 适配前必须先想明白的几件事
2.1 先摸清 sajilo 的核心机制再动手
很多人在鸿蒙适配时上来就翻源码、改代码,结果改到一半发现无从下手。正确做法是先搞清楚这个库在 Android/iOS 上是怎么运转的,哪些能力依赖平台侧,哪些完全跑在 Dart 层。这决定了你的适配边界到底在哪。
sajilo 的核心机制可以归纳为三个层面:
第一层是翻译资源管理。它不像 flutter_localizations 那样要求你在 pubspec.yaml 里声明 arb 文件,然后把资源打进包里。sajilo 更喜欢用 JSON 或者 Map 结构,可以在运行时从任意来源注入,比如 Asset 文件、网络接口、本地数据库。这一点是"动态注入"的基础。
第二层是文案查找与格式化。sajilo 会维护一个当前语言的翻译 Map,当你在界面上调用类似S.of(context).greeting这样的 API 时,它会根据当前语言环境去 Map 里查 key。支持参数插值,也支持复数形式。这层逻辑是完全跑在 Dart 侧的,不涉及任何平台能力。
第三层是系统语言监听。应用在后台被用户切换系统语言后,Flutter 需要感知到变化,刷新所有已构建的 widget。这一步在 Android/iOS 上依赖平台侧的能力回调,是标准化的 platform channel 操作。而 sajilo 在做鸿蒙适配时,最需要动手的就是这一层。
也就是说,整个库 70% 的代码是 Dart 层逻辑,可以直接复用,真正需要动手术的只有"读取系统当前语言"和"监听系统语言变化"这两个平台钩子。想明白这一点,适配的工程量就清晰了。
2.2 别急着写代码,先盘点鸿蒙侧的资源
OpenHarmony 上的 Flutter 开发环境和传统的 Android/iOS 有显著差异。当前社区主流的 Flutter SDK 是某个厂商或社区维护的分支版本,它在前端 API 上尽力保持与官方 Flutter 对齐,但底层已经完全替换成了鸿蒙的渲染引擎和平台抽象层。这就带来几个直接影响适配工作的差异点。
第一个差异是平台通道的命名空间。Android 上你用MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channelName)来创建通道,然后在 MainActivity 里注册。而在鸿蒙侧,你面对的是一个基于 ArkTS 的 Ability 环境,需要从flutter_ohos的 SDK 中拿ohos.flutter_ohos.FlutterEngine或者类似上下文,注册方式完全不同。
第二个差异是资源文件的获取路径。在 Android 上,你通过context.assets.open("translations/zh.json")访问 Flutter asset 是没问题的,但在鸿蒙侧,Ability 的资源管理机制走的是resourceManager的 rawfile 路径,如果你的翻译文件是作为 Flutter asset 打包的,平台侧想直接读取它就需要绕道。
第三个差异是生命周期回调。鸿蒙的 Ability 生命周期模型和 Android 的 Activity 不完全一致,比如页面进入后台、语言切换等事件的触发时机都有区别。如果你的桥接层没有处理这些差异,就会出现"应用切到后台再回来,语言没刷新"这种诡异问题。
所以在动工前,建议先把你依赖的鸿蒙 Flutter SDK 的底层接口文档翻一遍,把这三块能力的位置确认清楚。我在实际项目中在这一步省了时间,结果后面反复返工,教训很深刻。
2.3 评估三种适配路径的取舍
sajilo 的鸿蒙适配其实有三条路径可以走,渠道不同,成本和效果差异也很大。
第一条是 fork 源码直接改。把 sajilo 的仓库拉下来,在源码里新增一个ohos目录,仿照它的android和ios目录提供鸿蒙侧实现。优点是可控性强,缺点是你要长期维护一个 fork 分支,还要跟上游保持同步,否则上游更新了你这边就跟不上了。
第二条是写一个分包插件。保持 sajilo 原库不改,新建一个名为sajilo_ohos的独立包,通过 dart 的依赖注入机制或者平台接口替换的方式,在运行时覆盖掉原库的平台实现。这样原库可以继续走 pub.dev 的官方版本,你的分包只负责鸿蒙特异逻辑。缺点是分包对原库的结构有要求,如果原来的接口不够抽象,分包就会很别扭。
第三条是走通用的鸿蒙适配 SDK。有些大厂或开源社区已经沉淀了一套通用的 Flutter 鸿蒙支持库,把大部分 platform channel 的鸿蒙侧能力统一封装好了。如果你的项目恰好已经接入了这类 SDK,sajilo 的鸿蒙适配就只是在这个 SDK 之上补几个接口的问题。缺点是通用库的抽象度往往偏高,你可能会为了一个简单的语言监听接口引入一大堆无关能力。
我最终走的是第二条路径,用独立分包的方式适配,原因也很简单:sajilo 本身是极简路线,上游代码量不大,平台通道接口只有三四个,分包完全可以覆盖。这样既避免了长期维护 fork 的负担,又不至于被一个重型的通用 SDK 绑架。
3. 手把手实操:sajilo 的鸿蒙适配全流程
3.1 创建鸿蒙侧的插件工程
如果你用的是第二路径(独立分包),第一步就是创建一个新的 Flutter 插件工程,专门承载鸿蒙侧实现。这里注意,插件工程的命名要遵循 Dart 侧的项目命名规范,通常也叫sajilo_ohos,但它的平台目录里只需要放ohos这一块。
创建完工程后,你需要把鸿蒙 Flutter SDK 中添加的依赖关系配好。在工程下面的ohos/entry/oh-package.json5里,添加对 flutter 桥接 SDK 的引用:
{ "name": "ohos_entry", "version": "1.0.0", "dependencies": { "flutter_ohos": "1.0.0" } }同时,你需要在 Dart 侧声明一个平台接口。这个接口可以放在sajilo_ohos包里,也可以直接公开,让主包的sajilo依赖它。推荐的接口抽象是这个样子:
abstract class SajiloPlatform { static SajiloPlatform get instance => _instance; static SajiloPlatform _instance = MethodChannelSajilo(); Future<String?> getSystemLocale(); Stream<String> onLocaleChanged(); } class MethodChannelSajilo extends SajiloPlatform { static const MethodChannel _channel = MethodChannel('sajilo/locale'); @override Future<String?> getSystemLocale() async { return await _channel.invokeMethod<String>('getSystemLocale'); } @override Stream<String> onLocaleChanged() { return _channel.receiveBroadcastStream().map((event) => event.toString()); } }这个接口抽象出来的都是最小化的能力:一个是拉取当前系统语言,一个是订阅语言变化事件。剩下的翻译资源管理、文案格式化全部交给 Dart 层,平台侧不参与。
3.2 鸿蒙侧实现语言读取与监听
接下来是最核心的桥接逻辑。鸿蒙侧的能力入口是 Ability 的上下文,通过它拿到系统的资源管理器和配置信息。以下是一个基于 ArkTS 的简化实现思路。
import { MethodChannel } from '@ohos/flutter_ohos'; import { common } from '@ohos.app.ability; import { i18n } from '@kit.LocalizationKit; export class SajiloBridge { private context: common.UIAbilityContext; private channel: MethodChannel | null = null; constructor(context: common.UIAbilityContext) { this.context = context; } setup(channel: MethodChannel): void { this.channel = channel; this.channel.setMethodCallHandler(this.handleCall.bind(this)); // 注册配置更新监听 let configUpdateCallback = () => { this.channel?.sendEvent('localeChanged', this.getCurrentLocale()); }; this.context.on('configurationUpdated', configUpdateCallback); } handleCall(method: string, args: Object): Promise<any> { if (method === 'getSystemLocale') { return Promise.resolve(this.getCurrentLocale()); } return Promise.reject(new Error(`Unknown method: ${method}`)); } getCurrentLocale(): string { let displayInfo = i18n.System.getDisplayLanguage(this.context.config.language); return displayInfo; } }这段代码包含两个关键细节。
第一是configurationUpdated事件的注册。鸿蒙应用在系统语言变化时,会触发这个配置更新回调,你在这里把新 locale 通过通道发到 Flutter 侧,sajilo 的onLocaleChanged()就能感知到。
第二是i18n.System.getDisplayLanguage的使用。鸿蒙的context.config.language返回的可能是语言代码,但正常情况下应用需要的是可以用于匹配翻译资源的 BCP 47 标签,比如排掉地区差异的纯语言码zh、en。所以建议在返回前做一层标准化处理,把可能存在的地区信息去掉,避免zh-CN和zh-TW在匹配不到翻译时产生混乱。
3.3 桥接层的注册时机
很多适配都挂在同一个问题上:桥接层初始化太晚,导致 Flutter 侧在main()里第一次调用getSystemLocale()时就收到了空值。
鸿蒙 Flutter 应用的生命周期是这样的:入口 Ability 的onCreate()里会创建 Flutter 引擎和绑定的 Runner 页面;Flutter 引擎启动后,Dart isolate 里开始执行main()。如果你想保证 Flutter 侧一启动就能拿到系统语言,就必须确保SajiloBridge的 setup 在onCreate()阶段完成,因为 Dart 代码运行的速度非常快,如果你的 setup 被放在onPageShow或者更晚的阶段,Flutter 侧的第一轮渲染可能已经读取了空 locale。
实际的注册代码大致是:
// 入口 Ability 的 onCreate onCreate(want: Want, launchParam: AbilityLaunchParam): void { super.onCreate(want, launchParam); this.context = this.context; this.sajiloBridge = new SajiloBridge(this.context); // 这里必须拿到 flutter engine 的 binaryMessenger 去构建 MethodChannel let engine = this.getFlutterEngine(); this.sajiloBridge.setup(new MethodChannel(engine.getBinaryMessenger(), 'sajilo/locale')); }如果你的项目对初始化顺序有更高要求,可以设计一个 Dart 侧的"异步等待"机制,让S.of(context)在拿到第一个非空 locale 之前挂起框架层的构建。sajilo 本身是支持这种模式的,但是默认不开启,因为会增加首帧渲染的耗时。
3.4 在 Dart 侧完成动态注入的闭环
平台侧的语言读取做好后,真正体现 sajilo"动态注入"能力的是 Flutter 侧的这部分逻辑。这里我以简化的方式展示 sajilo 的核心架构。
sajilo 的翻译资源管理围绕一个S单例对象展开,你可以把它理解成一个可以在运行时被替换内容的内存字典。它支持三种方式的资源注入:
- 在应用启动时从 Asset 里加载一个完整的翻译 JSON;
- 在运行时通过接口拉取翻译片段并 merge 进当前字典;
- 在调试阶段直接通过代码写入单条 key 做热更新验证。
它的核心类结构可以简化成这样:
class S { static final S _instance = S._(); String _currentLocale = 'zh'; final Map<String, Map<String, String>> _translations = {}; void injectLocale(String locale, Map<String, String> entries) { _translations[locale] = entries; } void setLocale(String locale) { _currentLocale = locale; } String tr(String key, {Map<String, String>? args}) { var map = _translations[_currentLocale] ?? {}; var template = map[key] ?? key; if (args != null) { args.forEach((k, v) { template = template.replaceAll('{$k}', v); }); } return template; } }这个设计的特点在于没有强约束你必须用什么文件格式、什么目录结构,甚至是本地资源还是远端资源,它只管给你一个统一的查询入口。你在桥接层收到鸿蒙侧推送过来的localeChanged事件后,要做的只是两件事:
- 去资源源(可以是本地 json,可以是网络接口)加载对应 locale 的翻译 Map;
- 调用
S.setLocale(newLocale)并把变更通知给所有依赖S的 widget。
sajilo 内部是用一个InheritedWidget或者ChangeNotifier来做刷新通知的,具体实现不展开,你只需要知道这个机制是纯 Dart 的,鸿蒙适配不会对它有额外影响。
这里要特别强调一个动态注入的应用场景。假设你的产品运营想在某个国家的应用里临时投放一批英文文案,不改包就生效。传统做法是发版,但发版周期长且用户覆盖面有限。用 sajilo 的思路,你可以在应用启动后拉一次远端配置,把这个语言包的 JSON 直接 merge 到当前语言的 Map 里,然后刷新界面。整个过程用户无感,版本号都不用动。在鸿蒙生态里,应用分发审核链路更长,这个能力的价值就更加放大了。
4. 适配过程中的常见坑与排查实录
4.1 坑一:语言通道在模拟器和真机上表现不一致
鸿蒙侧的语言配置在模拟器和真机之间有明显差异。模拟器上,地区信息和语言信息往往被合并在一起返回,比如zh-CN会直接作为language返回;而在某些真机上,你拿到的可能是语言的短代码zh。这意味着如果你在 Dart 侧严格用zh-CN去匹配翻译 key,模拟器完全正常,一到真机就大量 key 落空,界面直接显示原始 key 字符串。
排查方法很简单:在桥接层加一段日志,把context.config.language的原始返回值打印到 Log 面板,对比一下模拟器和真机上的差异。然后统一在鸿蒙侧做标准化,不要把这个差异留给 Dart 层处理。标准化的逻辑就是:取语言部分,忽略地区差异,除非你的翻译确实需要区分zh-CN、zh-TW。
4.2 坑二:配置更新事件注册后不稳定,偶发漏事件
sajilo 的onLocaleChanged()流在鸿蒙上出现漏事件的情况,我们去排查后发现,问题不在通道本身,而是configurationUpdated回调的注册和注销时机。
如果你在setup()里注册回调,但在 Ability 的onDestroy()里没有正确注销,那么在页面销毁重建的过程中,旧的回调可能仍然持有已经失效的 channel 引用,导致新页面注册的回调被覆盖或者事件发不到 Dart 侧。
解决方式是建立严格的生命周期管理:
onDestroy(): void { this.context.off('configurationUpdated', configUpdateCallback); this.channel?.setMethodCallHandler(null); super.onDestroy(); }这是个小细节,但极其容易踩。我在联调时花了大半天,最后发现就是注销逻辑没做干净。
4.3 坑三:平台通道的二进制消息格式兼容
鸿蒙 Flutter SDK 是一套独立实现,虽然在前端 API 层面尽量对齐官方 Flutter,但 Messaging 的底层二进制消息格式偶尔会有些差异,尤其在跨版本升级的时候。sajilo 的通道用的是标准的MethodChannel和BasicMessageChannel,绝大多数情况下不会有问题。
但如果你在项目中同时用了其他第三方插件的平台通道,而那个插件又恰好没有做鸿蒙适配,会出现通道之间互相干扰的情况。这不是 sajilo 本身的问题,但排查时要先排除这个干扰因素。建议把所有第三方插件的初始化日志都打开,确认每个通道的 creation 和 handler 注册顺序是可控的。
4.4 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| Flutter 侧首次获取语言为空 | 桥接层注册晚于 Dart main() | 将 bridge setup 提前到 Ability onCreate |
| 切换系统语言后界面不刷新 | configurationUpdated 回调未注册或被注销 | 检查回调生命周期,及 channel 是否有效 |
| 模拟器正常但真机 key 落空 | 语言代码格式不一致 | 在鸿蒙侧统一标准化语言代码 |
| 通道发送事件偶发丢失 | 页面销毁重建导致 channel handler 失效 | 补全 onDestroy 的清理逻辑 |
| 中文文案显示乱码或编码异常 | 翻译 JSON 文件编码不是 UTF-8 | 统一转换为 UTF-8 编码 |
| 远端语言包 merge 后不生效 | 刷新通知机制未触发 | 检查 ChangeNotifier/InheritedWidget 的通知逻辑 |
| 首帧渲染卡顿 | 初始化时同步加载了过大的语言包 | 将资源加载改为异步,或做懒加载 |
4.5 我的调试心得
这里分享一个很实用的调试方法。鸿蒙侧没有像 Android 那么成熟的 debug 工具链,所以平台通道的联调建议走"双端日志交叉验证"的笨办法。
Dart 侧,在getSystemLocale()和onLocaleChanged()里都加上 debugPrint 输出,打印当前收到的语言代码和时间点;鸿蒙侧,在handleCall和sendEvent的地方也加上日志输出。两边的时间戳对齐后,就能很清晰地看出事件是在哪一侧丢失的。
我自己的经验是在调试阶段把日志调到最详细级别,等联调通过后再统一关掉,不要在调试过程中就追求日志精简,那样只会增加排查难度。
5. 热切架构的落地思考与扩展玩法
5.1 热切到底是什么,以及动态注入怎么和它组合
标题里提到"国际化热切架构",这个词这几年在移动端比较流行,核心含义是:应用的某些能力可以在运行时被"热"切换,不需要经过发版流程。对国际化来说,"热切"的颗粒度可以是语言包级别的,也可以是字段级别的。
sajilo 本身的设计就非常适合这个架构。因为它把翻译资源从代码逻辑中完全解耦出来,翻译文件就是一个可以随时替换的数据源。你可以在后端配置一个翻译管理界面,运营编辑文案后点击发布,客户端下次启动或者定时轮询时拉取到最新的 JSON,直接 merge 到当前语言 Map 里。
这意味着你的国际化文案体系变成了一个"配置中心",而不是"代码仓库"。在鸿蒙生态这种版本审核和分发链路更长的环境里,这种能力的价值被进一步放大——你不需要因为一个文案拼写错误就重新提审一个版本。
5.2 动态注入方案的架构设计建议
如果你的项目要参考这个方案落地,我给你梳理一个最小可行的架构分层,总共四层。
数据源层:可以支持本地 asset、远端接口、本地数据库三种来源。同一时间只选择一个主数据源,但支持优先级,比如"远端优先,本地兜底"。
加载层:负责从数据源读取原始 JSON 或 Map,做格式校验和编码转换。这里要做容错处理,比如远端数据拉取失败时,回退到本地最后一次成功的缓存。
管理层:sajilo 的S对象负责维护当前语言的翻译 Map,支持 merge、替换、清空等操作。这层是纯 Dart 的,不需要平台通道参与。
展示层:所有需要国际化的 widget 通过监听机制订阅语言变化。语言切换后,整棵 widget 树刷新。
这个架构在鸿蒙上跑起来后,我还额外做了一个实验:在应用不重启的情况下,通过模拟一套"远端下发"指令,把界面上的一行文案实时替换成了新的国家地区特有文案(带地区 emoji 后缀的那种),整个过程在 1 秒内完成,比重新发版效率高了一个数量级。
5.3 多语言之外,这套桥接能力还能做点啥
sajilo 的鸿蒙适配虽然是以翻译为切入口,但你搭建起来的这套平台通道桥接能力完全可以复用到其他场景。因为鸿蒙 Flutter 生态的插件还不完善,任何一个需要访问系统能力的 Flutter 库都可能面临和 sajilo 同样的适配问题。
举个例子,复制粘贴能力在鸿蒙上的实现逻辑就和 Android 不同,来电识别、消息推送、剪切板监听等能力也同样。你完全可以利用这次适配积累的桥接模式,把所有系统能力访问统一收敛到一个内部工具模块里,将来再适配其他第三方库时,只需做接口映射,不需要再研究鸿蒙底层。
所以在做适配时,我建议不要只盯着 sajilo 的 API,而是把"平台通道 + 生命周期管理 + 配置更新监听"这套模式沉淀下来。日后你会发现自己建的这套私房工具,价值远超原本那个库本身。
6. 性能、包体积与稳定性平衡
6.1 动态注入对包体积的影响控制
sajilo 走的是极简路线,Dart 侧代码量很小,它对包体积的增量几乎可以忽略。但真正要考虑的是如果后续做"远端翻译包下发"能力,本地缓存策略如果不加控制,翻译文件会越积越多,最终占用几十 MB 空间也不是没可能。
我的建议是:本地只保留当前语言和最近使用的一到两个备用语言的翻译包,更久远的清理掉。远端下发时在协议层直接带上语言版本号,如果版本号没变就不重新拉取,省流量也省解析时间。
以中文和英文两个语言包为例,每个包大约 5KB,一个中等规模的 App 三五十个语言的完整翻译包加起来也不到 1MB。这个量级对包体积的影响基本可以忽略,但如果你在翻译文案里嵌入大量富文本模板或者图片路径,体积会显著增加,需要单独评估。
6.2 切换语言的性能实测
我记录下来的一组实测数据可供参考:在一台中端鸿蒙设备上,从用户点击切换语言,到整棵 widget 树刷新完成,耗时大约在 180ms 到 350ms 之间。这个数值受两个因素影响:一是翻译 Map 的规模(key 数量越多,重构成本越高);二是当前界面的 widget 树复杂度。
如果你实测的切换耗时超过 600ms,可以从两个方向优化:一是把大的翻译 Map 拆成按模块懒加载的多个小 Map,只在对应路由第一次出现时注入;二是把刷新范围从"整棵 widget 树"缩减为"仅依赖语言变化的 widget 子树"。sajilo 默认是全量刷新,因为简单可靠,但性能敏感场景下值得做精细控制。
6.3 这条桥接链路的稳定性如何保障
平台通道的稳定性本质上是"通道生命周期管理"的稳定性。在鸿蒙上适配后,我总结出三个铁律:
第一,通道 handler 的注册必须在引擎启动阶段完成,不要在业务代码里临时创建临时通道,那样既慢又容易出问题。
第二,鸿蒙侧返回的数据必须保证是纯 JSON 兼容的类型。不要返回自定义对象,不要返回带复杂循环引用的结构,否则 Flutter 侧的编解码器很可能解析失败。
第三,通道事件流的关闭必须与 Ability 的销毁时机严格绑定,做好注销清理。如果你做的是一个长期运行的服务型应用,尤其要注意页面频繁进出场景下的资源释放。
7. 最后的实践经验总结
sajilo 鸿蒙适配这个项目做下来,我最大的感受是:任何一个 Flutter 库的鸿蒙化适配,本质上都不是在"写新代码",而是在"补桥"——你在补 Dart 世界和鸿蒙世界之间的那座原本缺失的平台桥。
这座桥不需要多宏大,关键在三点:把需要的平台能力接口尽量抽象得最小化;把生命周期和事件监听的时机精确对齐;把平台侧返回的数据结构严格标准化。只要这三点做到位,桥接层就会变得非常稳定,上层业务几乎感觉不到适配的存在。
我在实际编码过程中还有一个心得:不要追求一次把桥接层写完美,先把最小可用的通道打通,跑通一遍完整的"读取语言、切换语言、刷新界面"流程,再逐步补细节。很多团队在适配时想着一步到位,结果陷入鸿蒙底层 API 的细节里出不来,反而适得其反。
这次适配里还有个小技巧想分享:在调试阶段可以给 sajilo 加一个"模拟远端下发"的测试入口,在 Debug 模式下通过代码直接触发一次翻译 Map 的 merge 操作。这样你可以在不依赖真实服务器的情况下,快速验证动态注入链路是否正常。等验证通过后,把这个入口接入真实的后端下发逻辑就行。
希望这份适配指南对正在做鸿蒙 Flutter 适配的你有参考价值。如果你也在适配其他 Flutter 三方库时遇到类似的桥接问题,欢迎拿这套平台通道管理思路去举一反三。适配工作不性感,但它决定了你的应用在鸿蒙生态里能不能顺畅跑起来,值得认真对待。