ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

flutter_scripts 鸿蒙化适配实践:命令替换、构建改造与踩坑复盘

flutter_scripts 鸿蒙化适配实践:命令替换、构建改造与踩坑复盘 1. 先弄清楚 flutter_scripts 到底在管哪些事团队里的 Flutter 工程越来越多之后你会发现一个很现实的问题真正耗时间的不是写页面而是反反复复的工程治理。今天要清理缓存、明天要统一依赖版本、后天要给三个平台打不同的包、签名、传包每个动作背后都是一串命令行操作。手敲容易错漏一步就白等半天所以很多人会像我一样去折腾自动化脚本把常用的工程操作收敛成一条命令。flutter_scripts 就是这类工具的统称它本质上是围绕 Flutter 工程生命周期做管理的一组脚本集合覆盖了从工程初始化、依赖同步、代码生成、构建打包到产物清理的完整链路。我在第一次接触 flutter_scripts 时的直观感受是它像是一个工程管家的壳子。模块化做得比较清晰每个子命令负责一块独立事务比如 clean 只负责清理、doctor 只负责环境体检、build 只负责调用底层构建。这种设计有一个很直接的好处——你可以只挑自己需要的那几个命令接入现有工作流不用整个推倒重来。对我们这种既有存量工程、又要持续开新工程的小团队来说这个侵入性极低这也是我决定做鸿蒙化适配而不是另起炉灶写一套新工具的核心原因。但真正动手做鸿蒙适配之后才发现问题没出在脚本逻辑本身出在脚本底层默认绑定了 Android/iOS 的工具链。干净利落的adb、gradlew、xcodebuild调用来调用去到了鸿蒙工程里全都对不上号。鸿蒙侧的工程结构、构建命令、设备调试工具、签名机制都和安卓iOS不是一套东西如果直接把脚本拿过来跑八成会在执行到一半的时候以各种诡异报错收场。想让 flutter_scripts 在鸿蒙上继续当工程管家就必须搞清楚两件事第一这套脚本里哪些逻辑是跨平台通用的第二哪些命令需要被替换成鸿蒙侧的对等命令。下面我会按自己实际梳理的顺序把这套适配思路完整拆开讲包括环境准备、命令替换、构建改造、踩坑记录以及我们落地后的真实体感。2. 鸿蒙化适配的第一步把平台指纹从脚本里剥出来2.1 环境准备Flutter 与鸿蒙工具链的协作基础动手之前先把地基打牢。做鸿蒙侧的 Flutter 开发你需要的不是单纯的 Flutter SDK而是支持鸿蒙的 Flutter 适配版本配合 DevEco Studio 和对应的鸿蒙 SDK 使用。基本环境清单如下Flutter SDK鸿蒙适配分支具体版本号以官方发布为准DevEco Studio用于鸿蒙工程的编译调试内置 hvigor 构建工具Node.js 与 ohpm鸿蒙侧包管理器类似安卓生态里的 Gradle Maven 仓库角色hdc 命令行工具鸿蒙设备的调试工具功能定位等价于 adbhap-sign-toolHAP 包签名工具或者直接在 DevEco 中配置自动签名二选一即可我建议把hdc、ohpm、hvigorw这三个工具的路径统一加到系统环境变量里。刚开始做适配时因为偷懒没加后面写脚本时要在每个命令前补全路径又长又丑还容易遇到脚本在 CI 环境里跑不过去的问题。统一配好 PATH 之后脚本的写法可以干净很多跨机器执行的容错率也更高。环境验证上我习惯先手动跑一遍最简链路确认当前机器状态是好的再改脚本。命令如下flutter --version hdc --version ohpm --version如果这三条命令都能正常输出版本信息说明 Flutter 鸿蒙环境的基础依赖是通的。这里有个容易踩的细节有些环境里flutter命令能跑但创建鸿蒙工程模板时找不到对应的编译链十有八九是因为 DevEco 内置的 SDK 版本和 Flutter 适配分支的版本期望对不上。建议在动手写脚本之前先把 Flutter SDK 和鸿蒙 SDK 的版本号核对一遍省得后面排错排到怀疑人生。2.2 平台抽象把平台相关命令从脚本中剥离整理脚本的第一原则是把所有平台相关的命令调用集中管理不要散落在各处。我在适配时参考了很多开源脚本的做法最终收敛成一个config级别的平台映射表里面定义好每个抽象动作在当前平台下应该执行什么真实命令。比如脚本里原本用来清理安卓构建产物的逻辑直接调用了./gradlew clean到了鸿蒙工程里对应的清理动作变成了hvigorw clean。这两条命令在功能上等价但显然不能混用。我做的处理是把这类差异全部收进一个平台路由函数里# 平台路由函数示例 platform_build_clean() { case $PLATFORM in android) ./gradlew clean ;; ohos) hvigorw clean ;; ios) xcodebuild clean ;; esac }这样上层业务脚本不用关心底层平台差异只要调用platform_build_clean就能拿到当前平台下正确的清理命令。整个 flutter_scripts 改造的核心逻辑就一句话界面命令入口不变底层平台执行接适配层。实际执行时我把映射表单独放在一个platform_config.sh文件里方便统一维护。后续如果有人要新增 Windows 平台或者其他目标只需在这个文件里补一段路由而不需要去翻主逻辑脚本。这个结构对长期维护非常重要——脚本库最怕的不是功能复杂而是平台相关的脏逻辑到处都是改一次崩三处。2.3 核心命令对照表为了方便大家对照着改造我把 flutter_scripts 里最常出现的几个命令动作在 Android 和鸿蒙环境下的等价关系列成表格。注意这只是一个通用参考具体命令名称要以你本地安装的 SDK 版本为准。操作场景Android 环境等价命令鸿蒙环境等价命令构建产物flutter build apkflutter build hap工程级清理./gradlew cleanhvigorw clean设备调试连接adb deviceshdc list targets安装应用到设备adb install xxx.apkhdc install xxx.hap查看运行日志adb logcathdc hilog依赖包管理flutter pub getflutter pub getohpm install签名打包apksigner/jarsignerhap-sign-tool.jar测试执行flutter testflutter test部分适配层需确认注意鸿蒙侧的 Flutter 构建命令通常会依赖适配分支提供的特定参数比如指定 target platform 时可能要用--target-platform ohos之类的 flag具体请以 Flutter 鸿蒙 SDK 的文档为准。别想当然地认为flutter build hap一定存在如果你当前 Flutter 环境根本没拉鸿蒙适配分支这条命令直接会告诉你Unknown build command。在做命令替换时我强烈建议你在每个脚本模块里保留一份原始命令注释方便以后追查脚本行为。比如某段脚本原本跑的是adb reverse tcp:8081 tcp:8081适配后改为hdc reverse tcp:8081 tcp:8081但要在旁边注释一句原 adb 版本用于开发调试hdc 版本行为等价。这种细节看着琐碎但等一个月后你回来看脚本时会发现它帮你省掉了大量翻 git 历史的时间。3. 核心改造实录工程治理、依赖管理、构建打包三连击3.1 工程治理脚本垃圾清理与目录规划flutter_scripts 里最常用的模块大概率是工程清理。Flutter 工程跑一段时间后build目录、.dart_tool目录、.gradle缓存、oh_modules这些文件夹能占好几个 G 空间清理不及时不仅磁盘吃紧还会莫名其妙地触发各种缓存不一致的编译问题。原始安卓/iOS 环境下的清理脚本核心逻辑是调用flutter clean加手动移除几个已知目录。鸿蒙化之后这里多了一个oh_modules目录它是鸿蒙侧依赖的本地缓存根目录如果不清理某些依赖包的更新可能不会被正确拉取。我在适配时把 clean 模块的目录删除列表扩展为build.dart_tool.gradleoh_modules.hvigor各模块下的build目录具体实现上我不会直接写死这些目录路径而是从工程根目录递归查找。原因很简单鸿蒙工程往往是多 module 结构每个 module 都可能藏着独立的 build 目录只清根目录那个并不彻底。脚本里用find命令去匹配并删除效率也够高# 鸿蒙工程深度清理示例 find $PROJECT_ROOT -type d -name build -prune -exec rm -rf {} find $PROJECT_ROOT -type d -name oh_modules -prune -exec rm -rf {} 这里有个小坑oh_modules在鸿蒙工程里有时是符号链接指向公共目录如果find不加-prune处理可能会顺着链接目录钻进去删出一片狼藉。加-prune是让它在匹配到目标目录后直接跳过目录内部只处理目录本身。别小看这个细节团队里有同事用未加-prune的版本跑了三个月直到某天误删了公共依赖目录才暴露问题。清理完目录之后我还会顺手做一次flutter pub get加ohpm install把依赖重新拉一遍。原因是 Flutter 鸿蒙工程是双依赖体系Dart 侧依赖由 pub 管理鸿蒙侧的原生依赖由 ohpm 管理。单独清理掉一个而不重新同步工程打开时必然一脸红。把清理和依赖重装绑定在同一个命令里是我在实战中总结出的最佳实践——一次清理流程跑完工程直接进入可编译状态不用再手动干预。3.2 依赖管理脚本ohpm 与 pub 的双轨同步依赖管理在鸿蒙 Flutter 工程里比安卓环境要复杂一层。安卓 Flutter 工程只有 pub 管理的 Dart 依赖和 Gradle 管理的原生依赖鸿蒙 Flutter 工程的 Dart 依赖依然走 pub但原生层依赖对象变成了 ohpm。这意味着自动化脚本里同步依赖这个动作必须从单命令改成双命令顺序执行。我在 flutter_scripts 里为这个动作专门写了一个子命令执行顺序固定为flutter pub getohpm install --all不要试图把这两个命令并行执行。鸿蒙工程里部分模块的 ohpm 依赖在 pub get 之后才会生成正确的配置如果反过来或并行跑经常会看到module not found这类报错。保持串行不仅稳定排错时也更容易定位是哪个环节出了问题。依赖版本统一也是 flutter_scripts 的亮点之一。在安卓团队里多个 Flutter 工程常常各自维护一份依赖版本升级底层库时工作量巨大。flutter_scripts 里通常有一个版本检查模块会扫描所有工程的pubspec.yaml对比依赖版本是否一致。鸿蒙化之后这个模块的扫描范围还需要加上oh-package.json5文件否则鸿蒙侧依赖的版本漂移会成为盲区。这块我给的建议是不要只扫描不处理。脚本最好具备一键修复的能力——把不一致的依赖版本统一改成基准版本然后重新执行双轨同步。当然这种覆盖操作有风险所以在执行前脚本必须打印将要改动的文件列表并加上--dry-run参数支持预演。宁可多此一举也不能让自动化工具在不该自动的时候自动。3.3 构建打包脚本HAP 产物生成与签名构建打包是 flutter_scripts 鸿蒙化改造中最核心、也最容易出状况的部分。安卓环境下flutter build apk一把梭的体验很爽鸿蒙环境下flutter build hap的存在性取决于适配分支是否完整而且即便能构建成功HAP 的签名校验也比 APK 严格得多。我的构建模块在适配后长这样# flutter_scripts 鸿蒙构建子命令 flutter build hap --target-platform ohos --release \ --build-modeall-composite \ --sourcemap构建完成后需要确认产物路径。一般会在build目录下生成对应 module 的 HAP 文件但具体路径随工程结构浮动。脚本里我会用find动态搜索最近生成的.hap文件而不是写死路径这样工程结构变化时不需要同步改脚本。签名环节是另一个重点。HAP 签名需要准备三样东西p12 证书文件、profile 文件、证书库密码签名动作通常通过hap-sign-tool.jar执行。这里我先说结论如果团队暂时不需要手工签名建议靠 DevEco 的自动签名来兜底脚本只需在构建后生成产物即可签名交给 IDE。原因很实际——新接触鸿蒙的团队光梳理签名配置就要花半天时间而 flutter_scripts 的核心价值是压缩重复操作的耗时不是让你在签名参数里折腾。当团队对签名流程有把握了再把签名动作收进脚本让自动化程度更进一步。签名脚本的核心参数比较固定我大致列个结构参考java -jar hap-sign-tool.jar sign-app \ -keyAlias $KEY_ALIAS \ -signAlg SHA256withECDSA \ -keystoreFile $P12_PATH \ -keystorePwd $STORE_PASSWORD \ -appCertFile $CERT_PATH \ -profileFile $PROFILE_PATH \ -inFile $INPUT_HAP \ -outFile $OUTPUT_HAP签完名之后最好马上做一个校验步骤验证 HAP 签名是否有效。不要等到安装到真机才发现签名失效来回一趟的时间成本太高。校验命令一般是对应签名为verify-app具体参数可以参考 hap-sign-tool 的说明文档。3.4 联动 CI让鸿蒙构建跑进自动化流水线flutter_scripts 的落地场景不只是开发者本地CI 流水线才是自动化的最终归宿。鸿蒙构建跑进 CI 时要注意两个点环境隔离和命令超时。环境隔离是指 CI 上需要有独立的鸿蒙构建环境不能和安卓构建环境混用。原因很朴素——鸿蒙构建可能依赖特定版本的 Node.js、ohpm 和 hvigorw如果 runner 上还装着另一套安卓构建链环境变量冲突会带来大量不可复现的报错。我们团队的做法是给 CI runner 打上ohos标签只有鸿蒙构建任务会路由到这台机器上。命令超时是指 hvigor 构建首次运行时的耗时可能远超预期远高于 gradle 冷启动。第一次构建要拉取鸿蒙 SDK 依赖、初始化 hvigor 缓存我见过一个中大型工程首次构建耗时超过 15 分钟。CI 配置里的超时时间如果按安卓构建经验写个 10 分钟那鸿蒙构建基本必挂。建议把鸿蒙构建 job 的超时时间放宽到 30 分钟起步等 hvigor 缓存预热之后后续构建的时间会大幅回落。4. 踩坑记录鸿蒙构建中那些反常识的报错4.1 误用 Gradle 插件导致构建突变鸿蒙工程适配初期我遇到一个特别有迷惑性的报错报错内容大概包含这么一段You are applying Flutters main Gradle plugin imperatively using the apply script...看到这个报错的第一反应是 Gradle 脚本写错了于是我跑到工程里翻了一遍build.gradle文件却没发现任何手动 apply Flutter 插件的代码。排查到最后才发现这个报错根本不是当前鸿蒙工程报出来的而是脚本里残留的安卓构建命令在后台被触发了——flutter_scripts 的某个子命令里还保留了./gradlew assembleRelease之类的调用在鸿蒙工程根目录误执行了 Gradle 构建。这个错误给我最大的警示是平台抽象不只是把命令做映射还要在入口处做硬校验。我在适配后的脚本里加了一个保护机制执行构建类命令前先检查当前工程是否包含鸿蒙特征文件比如oh-package.json5是否存在。如果检测到了鸿蒙工程但命令路由指向的是安卓构建链直接中止执行并打印提示避免再出现跳过平台直接跑错命令的情况。4.2 hvigor 首次构建的假死现象鸿蒙工程的编译工具链是 hvigor第一次构建时它会拉取大量编译依赖日志输出看起来就像卡住了一样。刚开始我以为是脚本哪里写挂了反复 CtrlC 重试了好几次浪费了将近一个小时。后来冷静下来做了个对比实验不经过 flutter_scripts直接在 DevEco Studio 里手动构建同一个工程依旧要等那么久。这才确认是 hvigor 冷启动自身的特性并不是脚本逻辑问题。排查结论是hvigor 首次构建要下载约几百 MB 的编译链依赖如果网络状况不理想这个时间会被进一步拉长。针对这个坑我在 flutter_scripts 的构建模块里加了两个策略。第一构建前先执行一次ohpm install --all确保依赖已就绪第二给首次构建预留充足超时并在脚本中打印提示首次构建可能较久正在拉取 hvigor 编译链。这两个策略落地之后再也没有同事因为以为构建卡死而手动中断进程了。4.3 HAP 签名证书不匹配问题签名问题在 HAP 打包中尤为突出。APK 的 debug 签名可以用一个通用 debug keystore开发时完全不纠结但鸿蒙真机调试或发布时HAP 签名必须匹配对应的 profile 文件和应用 bundle 信息不匹配时构建可能成功但安装阶段会被拒。我遇到的具体场景是脚本里写死了固定证书换了一台新电脑后证书路径不存在脚本没报明显错误签名步骤生成了一个无效 HAP直到安装到真机才爆发。现在我的签名脚本都会在开头校验三个文件是否存在——证书、profile、私钥库只要有一个缺失就直接退出并提示。宁可早失败不要晚失败这应该是所有自动化工具的共识。4.4 设备连接与日志抓取的适配细节鸿蒙调试工具的默认行为和 adb 有不少差别。比如hdc list targets的输出格式和adb devices并不一样脚本如果沿用正则匹配安卓的输出格式大概率匹配不到任何设备。这种细节在文档里基本不会写只有跑到那一步才能发现。日志抓取也有差异。鸿蒙侧对应 adb logcat 的命令是hdc hilog而且 hilog 的日志格式、等级标签和 logcat 不同。如果你的脚本里有一段自动化日志分析逻辑这部分需要重新适配关键字匹配规则。我当时是被一个偶现的崩溃问题逼着去改了日志模块最后发现 hilog 里崩溃堆栈的关键字提示和 logcat 是两个风格硬套只会漏掉真正有用的信息。5. 自动化管家的完整体验从脚本工具到工程规范适配完成之后flutter_scripts 在我们这儿就不只是一个工具集合了它慢慢长成了团队工程规范的载体。我们有init、doctor、clean、deps-sync、build、sign、analyze-log等十几个子命令每个命令都对应一段工程操作的最佳实践。团队成员不需要记住一串串复杂的构建命令只要知道要出一个 HAP 就执行flutter_scripts build -p ohos门槛一下就降下来了。我整理了当前适配后的能力清单方便你对照自己的 flutter_scripts 做查漏补缺脚本模块适配前Android/iOS适配后鸿蒙落地效果工程初始化flutter create 模板支持生成鸿蒙目录结构新工程初始化从半小时压缩到 10 分钟内环境体检检查 Flutter 环境额外检查 hdc、ohpm、hvigor 环境环境问题早在开发前暴露清理工程清理 build 与 .gradle额外清理 oh_modules 与 .hvigor磁盘占用率明显下降依赖同步pub getpub get ohpm install 双轨同步依赖冲突问题减少构建打包apk/ipa 产物hap 产物 签名校验一键出包无需手动拼接命令日志分析adb logcat 解析hdc hilog 解析崩溃问题定位效率提升从团队落地角度说自动化脚本带来的最大收益不是节省了多少分钟而是把重复操作的确定性提上来了。人工操作最大的问题是每次都可能出现细微差异同一个命令有人加了参数、有人没加构建出来的产物行为可能就不一样。而脚本把参数固定下来之后所有人都用同一套标准出包问题复现和排查都变得简单很多。我特别建议你在团队内部推行 flutter_scripts 时把脚本版本纳入代码管理并且在 README 里写清楚每个子命令的适用平台。这样就算某一天团队来了新人他也能在十分钟内理解为什么鸿蒙构建要走这套脚本而不是对着历史文档翻半天。6. 适配鸿蒙过程中的一点个人体会踩过这么多坑之后我对 flutter_scripts 鸿蒙化的体会可以浓缩成一句话脚本本身是表平台抽象是里只有把平台差异收敛到配置层脚本才能在多平台之间游刃有余。这不是什么高深的技术就是工程治理里朴素的约定优于配置思路但越朴素的道理在落地时越容易被人忽略。最后分享一个实用的小建议在适配过程中给你的鸿蒙分支单独维护一份依赖清单文件里面记录当前 Flutter SDK 和鸿蒙 SDK 的版本组合。因为鸿蒙适配分支的更新节奏比主分支更快版本组合一旦错位复现问题的难度会成倍增加。有一份可追溯的版本记录至少能让团队在升级前心里有底。flutter_scripts 的鸿蒙化适配对我来说既是一次脚本改造也是一次对工程痛点的重新梳理。如果你也在做类似的适配不妨从自己的脚本里挑一两个最常用的子命令先跑通再逐步扩展。自动化工具的最终目标不是把所有操作都变成一键执行而是让你在真正需要介入时有足够的时间和精力去处理那些值得人工判断的事情。
返回列表