在RK3568开发板上把Flutter跑起来的经历,比我预想的要曲折得多。原本以为Flutter for OpenHarmony已经发展了好几个版本,装好SDK就能顺畅开发,结果光是设备树选型、SDK版本匹配就折腾了两天,等真机跑起第一个Hello World时,我才意识到这个生态里值得记录的东西太多了。这篇文章以我正在做的智慧学习助手App中的快速笔记功能为主线,把Flutter for OpenHarmony从环境准备、数据库设计、交互实现到平台能力调用、真机部署优化的完整链路写出来,尤其是那些搜索引擎里不太容易找到的坑。适合准备在OpenHarmony设备上做Flutter开发、或者想把笔记类工具移植到鸿蒙生态的开发者参考。
1. 开工前的关键决策:设备选择、SDK版本与工程初始化
1.1 RK3568设备树怎么选:同样是3568,开发板之间差异很大
先说设备树。这是我在OpenHarmony上遇到的第一个大坑。RK3568是一颗很常用的芯片,很多OpenHarmony开发板都在用它,但这并不意味着同一份固件能通吃所有板子。设备树(Device Tree)本质上是描述硬件资源的配置文件,屏幕分辨率、触摸IC型号、WiFi模组、内存大小、GPIO引脚分配全在里面。同样是RK3568,A开发板用的是MIPI DSI屏幕,B开发板用的是HDMI输出,C开发板可能接了eDP屏,设备树必然不同。
我一开始图省事,直接刷了手头开发板厂商提供的基础镜像,结果启动后屏幕完全黑着,串口日志刷了一堆面板初始化失败的报错。后来才意识到,OpenHarmony在启动阶段会通过设备树去匹配显示控制器和屏幕面板的时序参数,选错了就是黑屏,而且这种黑屏不像应用崩溃有日志,非常难排查。
踩过这次坑之后,我总结了一个相对靠谱的选型流程:先确认开发板的硬件版本,看底板丝印上的版本号;再找厂商提供的device目录,通常在OpenHarmony源码的vendor/你的板子/hardware/路径下,里面有board.json或类似配置文件描述使用的设备树;最后用串口工具接上开发板,在uboot阶段按任意键进入命令行,执行fbinfo或者查看内核启动日志里的Kernel command line,能直接看到加载了哪个dtb文件。
经验是:不要凭芯片型号相同就随意替换设备树,哪怕同一个厂商不同批次的产品,也可能因为屏幕驱动IC更换而需要不同的设备树。这块折腾一次之后,建议把开发板的设备树文件单独备份一份,后面编译内核或者升级镜像时还能对比,省得重新踩坑。
1.2 Flutter SDK与OpenHarmony SDK版本匹配的硬约束
设备树解决之后,紧接着就是开发环境。Flutter for OpenHarmony有一个很特殊的地方:你不能直接下载官网最新的Flutter SDK,必须使用OpenHarmony SIG社区维护的flutter_flutter仓库分支。这个分支会落后官方主线一段时间,而配套的Dart SDK、引擎版本、和OpenHarmony SDK之间都有一套固定的对应关系。
我的建议是先把以下组件准备好:DevEco Studio(建议5.0以上版本)、配套的OpenHarmony SDK、Node.js和ohpm包管理器,以及flutter_flutter仓库对应OpenHarmony-5.0版本的分支。安装完成后,执行flutter doctor,正常情况下会识别出OpenHarmony相关的条目。如果这里报错,大概率是环境变量DEVECO_SDK_HOME没有指向DevEco Studio自带的SDK目录,需要在~/.bashrc或~/.zshrc里显式配置。
版本不匹配的典型报错有两种。一种是Dart SDK版本冲突,比如你本地有其他Flutter项目用了一个较高版本的Dart语法,切到OpenHarmony分支后编译直接失败,报错信息会指向某个Dart SDK API不存在。另一种是编译HAP时找不到ohos SDK的platform版本,类似于Android开发里的compileSdkVersion不匹配。我的处理方式是严格参照flutter_flutter仓库README里标注的版本对应表,不要自己随便升级任何一环,否则后面依赖拉取阶段会连锁报错。
另外建议把OpenHarmony SDK的API版本固定下来,不要DevEco Studio一提示升级就升级。曾经遇到过一次,DevEco Studio升级后自动更新了SDK,结果Flutter引擎适配层还是旧版本,构建出来的HAP在真机上直接安装失败。后来强制回退SDK版本才解决。
1.3 初始工程的依赖锁定:避免"版本不对导致依赖包下不下来"
版本问题的第二波冲击发生在flutter pub get阶段。OpenHarmony分支的Flutter版本往往落后官方主线好几个小版本,而很多第三方插件在最新版本里已经依赖了官方主线的新特性,直接拉最新版就会报各种奇怪的约束冲突。
最开始我在pubspec.yaml里写了sqflite: ^2.0.0,结果pub get直接失败,提示某一个传递依赖要求Flutter版本大于当前版本。这种问题在纯Android开发里很少遇到,因为官方Flutter持续在向前迭代,但在OpenHarmony分支上非常常见。
我的解决方案是两件事。第一,针对常用插件维护一个版本清单,把经过验证的版本写到pubspec.lock里锁定,不要轻易整体升级。第二,在依赖解析失败时使用dependency_overrides强制指定某个传递依赖的版本,这个手段偶尔能救急,但需要注意可能会引入运行时问题,所以每次 override 之后都要在真机上跑一遍核心流程。
还有一类"依赖包下不下来"的问题跟代码版本无关,纯粹是网络源的问题。Flutter在国内环境的依赖下载经常超时,pub get卡在某个包上不动,然后报网络异常。这个问题的处理方式很常规:在环境变量里设置PUB_HOSTED_URL和FLUTTER_STORAGE_BASE_URL指向可用的国内镜像源。我自己用的就是社区里常见的镜像配置,设置完之后pub get的速度基本不会成为阻塞项。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据层设计:本地数据库与后端同步的取舍
2.1 为什么我最终选了"本地优先、异步同步"的架构
快速笔记这个功能,最核心的性能指标就是打开App就能写,输入过程不能卡,写了不能丢。这意味着数据写入必须走本地存储,而不是等网络请求回来再落盘。我在方案选型时对比了三种做法:直接通过Platform Channel调用OpenHarmony系统的关系型数据库(RDB)、用sqlite3的FFI封装、以及用sqflite的OpenHarmony适配分支。
最终我选择了sqlite3 FFI方案。原因有三:第一,OpenHarmony的RDB接口虽然官方支持,但它面向的是ArkTS原生应用,通过Platform Channel走Dart侧调用时数据序列化和反序列化开销不小,频繁插入笔记内容容易产生性能抖动;第二,sqlite3的FFI封装在跨平台生态里非常成熟,后续如果要把智慧学习助手同步移植回Android/iOS版本,数据库层的代码可以基本不改;第三,FFI方案绕开了sqflite插件在OpenHarmony上可能存在的适配滞后问题。
如果你团队的代码基础以原生鸿蒙为主,用RDB也是合理选择,但我的场景要求Dart侧逻辑可复用,所以把宝押在了sqlite3上。实测下来,在RK3568上连续插入100条笔记内容,单条插入耗时稳定在10ms以内,完全满足快速笔记的需求。
2.2 笔记表结构设计:软删除、时间戳、增量同步
数据库表结构直接决定同步逻辑的复杂度,这块我在设计时反复改过几版,最终固定下来的核心表结构是这样:
sql复制CREATE TABLE note (
id TEXT PRIMARY KEY, -- uuid,客户端生成
title TEXT NOT NULL DEFAULT '',
content TEXT NOT NULL DEFAULT '',
tag_ids TEXT NOT NULL DEFAULT '[]', -- 标签id列表,JSON数组存储
created_at INTEGER NOT NULL, -- 毫秒时间戳
updated_at INTEGER NOT NULL,
deleted INTEGER NOT NULL DEFAULT 0, -- 0正常 1已删除
sync_status INTEGER NOT NULL DEFAULT 0 -- 0待同步 1已同步 2同步失败
);
CREATE TABLE tag (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
created_at INTEGER NOT NULL
);
这张表有几个关键决策。主键用的是uuid不是自增id,这是为了让客户端离线创建记录时也能直接生成主键,不需要等服务端分配,从根源上避免同步时的主键冲突。deleted字段用来做软删除,因为真实的删除操作在同步场景里很难传达——如果客户端把记录物理删除,服务端就不知道这条记录曾经存在过,覆盖面较小的多端同步场景下,用一个标志位记录删除状态是最稳妥的。
sync_status是整个同步机制的晴雨表。每次本地写入或修改时,这个字段会被置为0,表示有待同步的数据变更。后台同步任务只需要查询sync_status != 1的记录,把它们推给服务端,再根据服务端返回值更新状态。这套"脏标记"机制非常简单,但非常实用,我在多个项目里都是这么玩的。
2.3 同步引擎的设计:先本地写,再异步推到后端
同步策略我采用了业界常见的"增量同步 + 最后写入胜出"方案,没有引入复杂的CRDT数据结构,因为快速笔记这个场景里,同一笔记同时被多端编辑的概率很低,就算发生了,保留最后写入版本也比强行合并更符合用户预期。
大致的同步流程是这样的:客户端任何写操作都先写本地库并标记sync_status=0,然后通过一个后台同步通道通知引擎有数据需要推送。同步引擎把待同步记录打包成JSON数组,附带一个client_version字段,POST到后端接口。后端收到后逐条校验,更新时间戳比本地新的记录,然后返回这批记录在服务端的最新状态。
拉取策略是对称的:客户端把本地最大的updated_at发给服务端,服务端返回所有大于这个时间戳的记录,客户端逐条应用,更新时间戳比本地新的记录。这个策略在网络环境差的时候也能有不错的容错,因为每次同步都可以从上一次的时间点继续,不需要全量重传。
当时我实际开发中踩过一个细节问题:如果客户端本地时间不准,比如开发板没有自动校时,updated_at字段会混乱。我的解决办法是在每条同步请求里额外携带服务端返回的时间校准偏移量,但实际操作中更省事的是直接用服务端时间戳覆盖客户端的updated_at。快速笔记这个场景不需要毫秒级的时间精度,所以这类取舍我基本都是选简单粗暴的方案。
2.4 并发写与事务:连续输入时崩溃的根源
这部分属于不到真机压测根本发现不了的问题。快速笔记有一个场景是用户连续快速打字,每停顿800毫秒自动保存一次,同时后台同步线程可能在推送或拉取数据。如果两个线程同时打开数据库连接执行写操作,sqlite会报SQLITE_BUSY或database is locked错误,严重时直接导致当前页面崩溃闪退。
定位这个问题花了不少时间,因为它是偶发性的,用户打字快一点就出现,慢一点就没事,日志里也没有明显的前置错误。最后是通过在DatabaseHelper里做了操作串行化解决的:所有写操作进入同一个队列,同一时间只有一个写事务在执行,读写通过BEGIN IMMEDIATE事务包裹。
dart复制Future<void> _synchronizedWrite(Future<void> Function() action) async {
await _writeLock.acquire();
try {
await action();
} finally {
_writeLock.release();
}
}
这里的核心思路是把并行写收敛为串行写,用锁保证数据库连接同时只有一个写入者。另外,我还在预览页面做了防抖,300毫秒内的连续更新会合并为一次数据库写入,既减少了锁竞争,也降低了IO频率,对RK3568这种性能并不算强的开发板来说,UI流畅度的提升非常明显。
3. 快速笔记的交互核心:自动保存、标签管理与编辑体验
3.1 打开即写:自动聚焦与防抖自动保存
快速笔记的交互体验跟普通笔记App有本质区别:普通笔记需要用户先选择笔记本、再新建页面、然后开始编辑;快速笔记则要求打开App的瞬间就能输入。我实现的交互链路是:冷启动App后首页直接进入一个空白笔记页,FocusNode自动请求焦点,键盘弹出,用户就可以打字了。
自动保存的实现用了防抖模式。用户每次输入都会触发onChanged回调,我设置一个800毫秒的Timer,每次输入都取消上一次的定时器再重新计时。这样做的好处是,用户连续打字期间不会频繁触发数据库写入,只有停顿超过800毫秒才保存一次,IO量大幅下降。
dart复制void _onContentChanged(String value) {
_debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 800), () {
_saveNote();
});
}
保存状态我用一个三态指示器展示在AppBar上:输入中显示"未保存",正在写入数据库时显示"保存中",写入完成后显示"已保存"。别小看这个状态提示,它给用户的确定性反馈能显著降低"我输入的东西到底存了没有"的焦虑。实测数据上,从输入停止到状态栏显示"已保存"大约需要50到100毫秒,体感几乎是即时的。
3.2 标签筛选的UI实现:CheckboxListTile文字间距问题
标签功能在笔记类App里是刚需,我在设置页做了一个标签管理界面,用户可以创建标签,也可以在编辑笔记页通过一组复选框给笔记打多标签。用的是Flutter自带的CheckboxListTile组件,但很快就遇到了网上很多人问的"checkboxlisttile 文字距离按钮"问题——默认样式下,复选框和文字之间的间距在各种设备上渲染不一致,尤其在OpenHarmony真机上,间距明显偏大,整体视觉非常松散。
这个问题的根因是CheckboxListTile内部使用的是ListTile的布局逻辑,contentPadding、dense、visualDensity这些属性共同决定了复选框和文字的相对位置。不同平台的默认visualDensity不同,OpenHarmony上的渲染又跟Android不完全一致,所以才会出现间距忽大忽小。
我的解决方案是直接放弃CheckboxListTile的默认布局,改用Row + Checkbox + Expanded组合成自定义列表项:
dart复制Row(
children: [
Checkbox(
value: selected,
onChanged: onChanged,
visualDensity: VisualDensity.compact,
),
Expanded(
child: Text(
label,
style: const TextStyle(fontSize: 16),
),
),
],
)
这样复选框和文字的间距就完全由Row的布局控制了,在各平台表现一致。另外补充一个小知识点:如果文字很长需要换行,Expanded包裹后Text会自动换行,而Checkbox固定在最左侧,不会出现文字把复选框挤没的情况。
3.3 从新建笔记到一条完整记录的完整链路
把上面的模块串起来,一条笔记从诞生到同步到后端,完整链路是这样的:
- App启动,进入首页,自动新建一条空白笔记记录,
id由客户端生成uuid,created_at写入当前时间。 - 用户输入标题和正文,输入停顿800毫秒触发自动保存。
- 用户在标签栏勾选已有标签或创建新标签,多标签以JSON数组形式更新到
tag_ids字段。 - 点击图片按钮插入一张学习资料截图,图片保存到应用私有目录,图片路径记录在
content的Markdown语法里。 - 上述所有写操作完成后,将
sync_status置为0,后台同步引擎收到通知。 - 同步引擎将这条笔记连同
updated_at时间戳推送到后端,后端返回成功后sync_status置为1。
这条链路里最容易出问题的环节在第二步到第五步之间:如果用户在保存过程中就开始点其他按钮,或者同步线程恰好在写库,就很容易触发前面提到的SQLITE_BUSY问题。我在真机上反复测试后,把数据库写操作和同步操作全部纳入了同一把异步锁,才彻底解决偶发崩溃。
4. 平台能力打通:图库选图、微信登录、IAP支付的鸿蒙适配
4.1 通过Platform Channel调用鸿蒙图库选图
快速笔记需要支持插入图片,在OpenHarmony上,Flutter侧不能直接用image_picker插件,因为它的原生实现走的是Android的Intent机制,鸿蒙根本不认。我改用MethodChannel调用OpenHarmony的PhotoAccessHelper。
Flutter侧代码:
dart复制static const MethodChannel _channel = MethodChannel('learn_assistant/gallery');
Future<String?> pickImage() async {
try {
final String? uri = await _channel.invokeMethod('pickImage');
return uri;
} on PlatformException catch (e) {
debugPrint('pick image failed: ${e.message}');
return null;
}
}
OpenHarmony侧(ArkTS)代码:
typescript复制import { photoAccessHelper } from '@kit.MediaLibraryKit';
const helper = photoAccessHelper.getPhotoAccessHelper(context);
const pickerConfig = new photoAccessHelper.PhotoViewPickerConfig();
pickerConfig.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
const selectResult = await helper.selectPhoto(pickerConfig);
return selectResult.photoUris[0];
这里有一个非常关键的细节:通过PhotoViewPicker返回的URI是临时只读权限,App只能在这个权限窗口内读取图片数据。如果你把它存到content字段里,下次App重启后这个URI就失效了。正确做法是拿到URI后立刻把图片文件复制到应用的私有目录,然后保存私有目录的文件路径。我因为在初期没有注意到这个权限窗口问题,导致用户重启App后正文里的图片全部裂开,排查了好久才找到原因。
4.2 微信登录与IAP支付的兼容性经验
智慧学习助手需要用户体系,我接入了微信登录。微信开放平台已经支持鸿蒙应用类型,但集成的坑比Android多。首先需要在微信开放平台创建鸿蒙应用并绑定包名,这个包名要和HAP的bundleName完全一致;其次,鸿蒙端的微信SDK初始化需要一个Context,不像Android的Application那么简单,需要在EntryAbility的onWindowStageCreate里做初始化;最后还要配置签名信息,鸿蒙的签名指纹跟Android不同,需要在DevEco Studio里查看并填入微信平台。
实际开发中,微信登录在OpenHarmony上最典型的失败场景是拉起微信后回调不回来,或者提示签名校验失败。前者一般是关联域名或回调方式配置问题,后者基本就是签名指纹填错了。调试这个功能建议直接跑release包,因为debug包的签名跟release包不同,微信回调机制对签名校验非常严格,用debug包拉不起来是正常的。
IAP支付方面,如果应用需要上架到华为应用市场,支付能力需要走华为IAP SDK,而不是Google Play的结算库。Flutter侧同样通过MethodChannel封装调用,ArkTS侧初始化IAP客户端,拉起商品列表和购买流程。需要提醒的是,支付功能在开发阶段无法完整验证,因为IAP设计上要求使用正式签名的包才能正常拉起购买页,所以建议把支付模块做成可配置的mock模式,方便联调其他业务。
4.3 OpenHarmony权限声明与动态授权
调用图库、访问网络、读取设备信息这些操作都涉及权限申请。OpenHarmony的权限模型跟Android类似,需要在module.json5里声明权限,某些敏感权限还需要运行时动态申请。我的经验是,把权限申请统一封装到一个工具类里,在应用启动时集中处理必要权限,而不是在用到某个功能时才弹窗询问,后者在Flutter页面层级上实现起来很别扭,用户体验也差。
实际开发中一个比较隐蔽的问题是:ohos.permission.READ_IMAGEVIDEO权限未授权时,PhotoViewPicker的行为不是直接报错,而是弹出一个系统提示后返回空结果。如果不仔细看返回结果,很容易误判为图库选图功能有bug。我后来在处理逻辑里加了对空结果的明确提示:"未选择图片或没有相册权限",才算把这个问题暴露出来。
5. 真机部署、性能优化与踩坑复盘
5.1 从模拟器到RK3568真机的部署流程
OpenHarmony的模拟器我没有跑起来过(当时GPU加速支持不理想),整个开发过程基本都在RK3568真机上完成。真机调试的第一步是连接设备,OpenHarmony使用hdc工具(类似Android的adb),先通过USB连接开发板,执行hdc list targets确认设备被识别,然后hdc shell进入设备命令行。
构建HAP包的命令是flutter build hap --release,跟Android的flutter build apk流程对应。但HAP必须经过签名才能安装,Flutter工程模板默认带一个debug签名配置,开发调试阶段没问题,如果要安装到别人的设备或者准备上架,就需要自己在build-profile.json5里配置签名证书。这部分建议直接在DevEco Studio里的Project Structure界面操作,比手动编辑JSON文件直观得多。
安装命令是hdc install path/to/your.hap,也可以直接用DevEco Studio的Run按钮一键部署,它会自动执行构建、签名、安装、启动的完整流程,开发效率高很多。
部署阶段我遇到过一个比较头疼的问题:第一次安装成功,第二次改代码后重新构建,安装时报"INSTALL_PARSE_FAILED_INCONSISTENT_SIGNATURE"错误,提示签名冲突。原因是DevEco Studio自动签名时生成了一次性证书,卸载重装后证书变了,跟已安装的应用签名不一致。解决办法是卸载旧应用再安装,或者配置一个固定的签名证书。
5.2 首帧提速与包体优化
RK3568的性能比主流手机弱一些,首帧启动时间如果超过2秒,用户的耐心就会受影响。我针对首帧做了三件事。
第一件事是把数据库操作从主Isolate挪到后台Isolate。快速笔记功能启动时需要初始化数据库、创建表结构、读取标签列表和最近笔记,这些IO操作走主线程会阻塞UI渲染。我用Isolate.run封装了数据库初始化逻辑,启动阶段UI先渲染空内容,数据准备好后再通过状态管理更新页面,首帧从1.8秒降到了1.2秒左右。
第二件事是延迟加载非关键资源。登录态检查、同步引擎初始化、标签云加载这些功能不在首页必需显示逻辑里,我把它们放到WidgetsBinding.instance.addPostFrameCallback里延迟执行,确保首帧渲染不被打断。
第三件事是字体加载。OpenHarmony系统字体文件和Android不完全相同,Flutter引擎首次启动时需要做字体回退和测量,这个耗时在低端设备上非常明显。我不仅设置了常用的fontFamilyFallback列表,还尽量避免在启动页使用非默认字体。
包体优化方面,Flutter的HAP包默认会包含所有平台的支持库,体积动辄上百MB,对OpenHarmony设备不太友好。我在pubspec.yaml里启用了flutter pub deps --style=compact检查依赖树,删掉不必要的插件;同时将图片资源通过flutter_launcher_icons和flutter_native_splash统一处理后,开启Tree Shaking Icons,把只使用到的小部分Material图标编译进包,几乎不影响开发体验。
5.3 Gradle插件报错的排查思路
开发过程中遇到的一个很典型的报错信息是:you are applying flutter's main gradle plugin imperatively using the apply s...。这是一个Flutter自身升级后不够兼容的问题,在OpenHarmony的Flutter工程模板里同样会触发。
先说这个报错的本质。Flutter新版本推荐Android工程通过plugins DSL的方式声明Flutter Gradle插件,也就是在settings.gradle里用plugins {}加上id "dev.flutter.flutter-plugin-loader"这样的写法。但很多第三方库或者老项目仍然在用apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"这样的命令式方式挂载插件,两种方式混用就会触发这条警告,在特定配置下还会直接编译失败。
我的排查思路是这样的:先看报错出现在哪个模块的构建脚本里,通常定位到android/app/build.gradle文件;检查文件头部是apply plugin:这种命令式语法,还是plugins { id ... }这种DSL语法;把命令式语法统一改成DSL写法。具体到OpenHarmony工程,harmony/目录下的构建配置也要同步检查,确保Flutter插件加载方式和OpenHarmony的HAP构建插件不冲突。
这个问题在OpenHarmony上比在Android上更麻烦的地方在于报错日志不够直观,有时候编译到一半直接退出,没有任何堆栈。我的处理方式是把flutter build hap命令加上-v参数跑verbose日志,可以看到每一步具体在执行什么任务,定位是哪一步崩的。
5.4 热重载失效时的替代调试方案
Flutter引以为傲的热重载在OpenHarmony上的表现不太稳定。我实测发现,纯Dart代码改动热重载成功率还可以,但一旦改动了原生侧代码或者改了发布配置,热重载就彻底失效,必须重新构建HAP安装,一次完整的构建加安装循环在RK3568上要花一到两分钟。
这个效率瓶颈逼着我调整了调试策略。第一,把所有核心业务逻辑,比如同步状态机、防抖保存策略、标签数据过滤这些纯逻辑功能,尽量写成不依赖Flutter框架的纯Dart类,这样可以直接用flutter test跑单元测试,不需要部署到真机就能验证正确性。第二,页面的UI状态管理尽量用ChangeNotifier或Provider这类可脱离Widget树测试的方案,把UI层和数据层彻底解耦。第三,启用debugPrint的日志分级,在设备端实时查看日志,RK3568的真机日志输出偶尔有延迟,但整体是可用的。
还有一个技巧:在main.dart里做一个测试环境开关,通过--dart-define=APP_ENV=dev或=prod控制应用在开发环境和生产环境之间的切换。开发环境可以输出详细日志、开启mock登录、关闭同步真推,这样即使热重载失效,每次重新安装后我也能快速进入可调试状态,不用每次都走完整用户流程。
关于包体安全和反调试,虽然HAP包被解包后能看到Dart层的代码结构,但纯Dart层的字符串和数据逻辑还是能分析出来的。针对这一点,我至少建议开启代码混淆:flutter build hap --release --obfuscate --split-debug-info=build/symbols,这样符号表单独存放,发布包里的类名和方法名会被混淆,能在一定程度上提高和逆向分析的门槛。
最后再分享一点个人体会
整个智慧学习助手快速笔记功能从环境搭建到真机部署,前后大概花了两周时间,其中真正写业务代码的时间只占一半,剩下全在跟环境、版本、设备树和平台差异作斗争。OpenHarmony上的Flutter生态跟Android/iOS相比还远不算成熟,但你一旦把开发环境稳定下来、把依赖版本锁定、把设备相关的坑记到项目README里,后续的开发节奏会顺畅很多。
根据我自己的实际经验,有三件事值得每个准备在OpenHarmony上做Flutter开发的人提前做好:一是花时间整理一份"版本锁定清单",把Flutter分支版本、OpenHarmony SDK版本、关键依赖版本、设备树文件路径都记下来,团队协作时能省掉大量沟通成本;二是把设备树和固件备份好,OpenHarmony系统升级或者SDK更换后,随时可以回滚对比;三是在项目一开始就建立好flutter test单测体系,把业务逻辑放在纯Dart层,尽量减少对真机的依赖。做到这三点,你在RK3568上的Flutter开发体验会明显好于大多数人。
