定制侠(Flutter + OpenHarmony)鸿蒙端进入真机验证阶段。这一天的目标只有一个:把装到手机上的包从「白屏」救到「功能可用」。过程中踩了四个叠加的坑、返工三次,最终华为账号登录、微信登录、带图分享全部跑通。
环境:HarmonyOS 商用 SDK(API 26 编译,真机 HarmonyOS 6.1 / API 23),Flutter OHOS 3.41.9。
现象很典型——hap 安装成功、进程活着、界面纯白。这类问题难在「没有任何一条致命日志」,只能靠逐层剥离。
.dart_tool/package_config.json 仍指向本机 D 盘旧 Flutter(Dart 3.6.2)解析出的依赖路径,产出的 kernel 字节码版本是 v120,而 F 盘新 SDK 引擎期望 v127。版本对不上,引擎加载失败。
处置:用 F 盘 SDK 重新 pub get,并在构建脚本里做 PATH 加固(新 SDK 路径前置),杜绝外部 flutter 命令再次污染。
工程路径含中文,构建中间目录里残留了 URL 编码后的路径片段,导致增量构建复用了错误产物。
处置:删掉 defaultRelease 中间目录重建。
ohpm 依赖模块中生成的 BuildProfile.ets 把构建模式写死为 DEBUG,release 构建下这些模块仍按 debug 分支执行。
处置:加 patch_buildprofile.py,构建前统一改写。
oh-package.json5 的 overrides 写死了 debug 引擎 har,libflutter.so 38.9MB。而 debug 引擎带 TRACING_CHECKS_NECESSARY 检查,要求启动时传 --enable-checked-mode(只有 flutter run 才会传),桌面/hdc 直接启动会被拒绝创建引擎,于是秒退白屏。
处置:加 switch_engine.py,按构建模式切换 native so(release 引擎 16.4MB),构建后自动还原。
排查抓手:白屏/秒退优先抓
hilog里的XComFlutterOHOS_Native与FlutterNapi标签。
四层全部修完后产出 rel4(36.8MB),真机验证:登录页完整渲染、进程稳定。
有个很自然的疑问:打包不是应该用 flutter build 吗,为什么要直连 hvigorw?
实测回答了这个问题。跑 flutter build app --release,在 assembleApp 阶段崩在两个点上:
The srcPath is not a relative path。fluwx/tobias 是 hosted 包,被解析回 D 盘旧缓存,Node 的 path.relative 跨盘无法相对化,产出绝对 srcPath,hvigor 拒收。hvigorfile.ts 含 flutter-hvigor-plugin),flutter build 走 OhosHvigorBuilder 直连 hvigor,不执行 flutterBuildPre——不复制引擎 har、不装 ohpm 依赖。实测证据:ohos/har/ 未创建、oh-package.json5 未被改写、oh_modules 里仍是 debug so。也就是说标准命令对本工程零增益。绕开是有代价的:正是 flutterBuildPre 里的 copyFlutterRuntime 负责按模式复制引擎 har,跳过它就埋下了上面第 4 层根因,switch_engine.py 本质上是在手工补回这一步。
长期最优解是把 ohpm/pub get 链路修好后回归 flutter build ohos,三个补丁脚本即可退役。
既然 00303231 的根源是 hosted 包跨盘解析,就把它们改成 path 依赖,指向本地已配好鸿蒙实现的副本,重新 pub get。改完后 .flutter-plugins-dependencies 自动指向新路径,不再需要手工修补。
途中补了两个洞:
lib/src/ 与 android/src/ 被剥离,改 path 后 Dart 编译期直接报类型不存在。已从完整版补全。产出 rel6,真机验证通过,00303231 彻底消失。
定制页分享出去只有文字,图片丢了。
查 SDK 文档看到 SharedRecord 的约束「Either content or uri must be set」,判断是同一条 record 里同时塞了 uri 和 content,ShareKit 按文本处理、图片被丢弃。于是把 record 拆开——没修好。
用户反馈:老版本是正常的,能分享出图片。
对比旧工程后发现了此前一直没注意的事实:两边根本不是同一份代码。
| 旧工程(能出图) | 当前工程 | |
|---|---|---|
| share_plus 来源 | git 依赖,鸿蒙适配 fork 分支 | pub.dev hosted 版 |
| ohos 实现 | 完整真 ShareKit 实现 | stub 空壳 |
版本号都是 10.1.1,但实际代码天差地别。hosted 版的 ohos 实现是空壳,所以「去 stub」时等于自己重写了一份——这才是写法不同的来源,不是把旧版改坏了。
在插件里埋了 ERROR 级诊断后,日志给出决定性证据:
Share --> shareFiles
Share --> DZDIAG paths=1 | EXCEPTION={}
Share --> DZDIAG result paths=1 | EXCEPTION={} | uris=0
Share --> shareFiles uris count:0
HuaweiShare: DataUtils no valid uris
paths=1 之后立刻抛异常——取第一个路径时就炸了。原因是:MethodChannel 里 call.argument('paths') 从 Dart 侧过来的是普通 JS Array,不是签名上声明的 @ohos.util.List。为修复 forEach(async) 不等待的问题,取值被改成了 paths.get(i),而 Array 上没有 get,直接 TypeError,于是 uri 一个都没拿到,退化成纯文字。
旧版用 forEach 恰恰绕开了这个坑。
paths.forEach 同步收集(对 Array 和 List 都通用,且回调内无 await 所以是真同步),再用 for + await 逐个复制——既兼容真实类型,又修掉了异步不等待。产出 rel10,真机验证:分享正常带图。
JSON.stringify(Error) 打异常。Error 的 message 不可枚举,结果恒为 {}。这次前两轮返工完全看不到错误原因,就是栽在这上面。应该打 err.message 加 err.stack。List<string>,实际传过来是 JS Array。取值别用 .get()。compatibleSdkVersion 必须不高于设备 API,且版本串格式随 API 分档:API 10-25 用 主.次.修订(API) 形式,API 26 及以上才用 26.0.0。T | null 变量赋值后再调用其方法,类型会被窄化为 never 而编译失败。应先收集数组再构造对象。| 功能 | 状态 |
|---|---|
| 启动渲染 | 通过 |
| 华为账号登录 | 通过 |
| 微信登录 | 通过 |
| 分享带图 | 通过 |
| 支付宝拉起 | 待真机验证 |
构建链路仍是直连 hvigorw 加三个补丁脚本(切换引擎、补丁 BuildProfile、切换签名)。待 ohpm/pub get 链路修好后可回归标准命令并让补丁脚本退役。
还没有评论,登录后来说两句。