首页 / 资讯中心 / 文章详情

鸿蒙Flutter工程硬编码扫描:string_literal_finder适配实践指南

鸿蒙Flutter工程硬编码扫描:string_literal_finder适配实践指南 ★ FEATURED ARTICLE
做跨端开发有些年头了Flutter 项目从 0 到 1 跑过好几个Android、iOS 两端的工程治理也积累了不少经验。但真正让我头疼的是半年前把一套 Flutter 工程往鸿蒙上迁移时发现过去依赖的三方工具链几乎全部要用“鸿蒙的思路”重新过一遍。其中最有代表性的就是 string_literal_finder——一个用来扫描硬编码字符串、强制国际化规范的 Dart 命令行工具。它在 Android/iOS 生态里用得顺风顺水到了鸿蒙 Flutter 工程里直接跑往往水土不服。这篇文章我就把整个鸿蒙化适配过程拆开揉碎讲清楚包括工具原理、适配路线、扫描规则、CI 集成和踩坑实录给同样在做鸿蒙 Flutter 工程治理的团队一个能直接落地的参考。这篇指南适合三类人看正在把 Flutter 工程迁到鸿蒙、又不想放弃工程规范的同学在鸿蒙原生 Flutter 混编场景下做代码质量治理的团队成员以及纯粹对“如何把一个成熟三方库改造到新平台”感兴趣的开发者。看完之后你不仅能跑通 string_literal_finder还能根据自己的工程形态定制一套硬编码扫描方案。1. 先搞懂 string_literal_finder 到底在做什么1.1 它的核心机制不是正则硬扫而是走 Dart 语法分析很多人以为这种扫描工具是拿正则去匹配引号里的内容简单粗暴。真这么做的工具误报率高得没法用。string_literal_finder 走的是另一条路它借助 Dart 官方的 analyzer 库对代码做 AST抽象语法树级别的分析在语法树里精确定位 StringLiteral字符串字面量节点然后再套一层规则过滤。这带来的直接好处是它天然理解 Dart 语法。比如字符串插值 hello $name它会识别出这是个整体字符串比如代码里出现的注解、import 路径、注释内容都不会因为“长得像字符串”就被误报。核心扫描逻辑是平台无关的这就为鸿蒙化适配留下了可能性——难点根本不在 Dart 层面而在工程形态和接入方式上。1.2 它的默认能力边界在哪里工具本身提供的主要能力包括扫描指定目录下的 Dart 文件报告所有未被忽略的字符串字面量支持用 ignore 规则把特定模式的字符串放进白名单支持排除指定路径比如生成代码目录支持输出多种报告格式默认是控制台也可以落成文件。但注意它的默认认知是“你扫描的是一个标准 Flutter/Dart 工程”。鸿蒙 Flutter 工程虽然核心还是 Dart但工程结构、配置方式、构建链路都变了直接套默认行为会有三个问题第一扫描目录不符合鸿蒙工程的资源组织方式第二大量自动生成代码需要额外排除否则报告会被噪音淹没第三鸿蒙侧还有 ArkTS 代码string_literal_finder 默认不处理硬扫也不是不行但结果不可控。后面我会一步步拆这些问题的解法。1.3 鸿蒙 Flutter 工程和 Android Flutter 工程差异对照在聊适配方案前先用一张表把鸿蒙 Flutter 工程和传统 Flutter 工程的差异列清楚这是后面所有决策的基础。维度传统 FlutterAndroid/iOS鸿蒙 Flutter 工程工程组织单 Flutter modulelib/ 下基本是 Dart 源码DevEco Studio 工程结构包含 entry 模块、oh_modules 依赖目录Dart 代码通常放在 entry/src/main/ets 之外的 flutter 模块自动生成目录.dart_tool、build、.g.dart 等还要额外排除 oh_modules、entry/build、.hvigot、.cxx 等资源管理Flutter 资产assets、本地化 .arb 文件鸿蒙原生资源走 resources/base/element/string.json两套体系并存构建工具链dart / flutter 命令即可需要 DevEco Studio 的 hvigor 构建环境变量多了一套鸿蒙 SDK 路径混编情况主要是 Dart 原生平台代码Flutter ArkTS 混编是常态.ets 文件里的字符串同样需要治理依赖拉取pub.dev 或内部 pub 源鸿蒙开发通常配置 gitee 或其他镜像源三方库获取路径有差异看完这张表你就明白工具本身不需要大改但接入侧要做的事比想象中多。2. 鸿蒙化适配路线不要一上来就 Fork 源码2.1 三条路线怎么选面对“工具在鸿蒙工程里跑不顺手”直觉反应是 fork 源码改逻辑。我劝你先冷静。string_literal_finder 的核心扫描逻辑与平台无关真正不兼容的是“工程环境认知”。从这个判断出发有三条路线路线 A直接 fork 三方库改源码。适合工具本身有 bug 或缺少关键能力的情况。缺点是后续上游更新难同步而且鸿蒙公版 SDK 的更新节奏很快fork 版本容易越维护越落后。路线 B给工具做一层 CLI 封装。不碰工具源码写一个 shell 脚本或者 dart 脚本负责探测鸿蒙工程结构、组装参数、补扫 ArkTS 文件、汇总结果。这是成本最低、最稳的方案。路线 C把工具能力集成到更大的治理体系里。比如在 CI 流水线里串起多个检查工具string_literal_finder 只是其中一环。这个适合团队已经有工程效能平台的情况。我最终选了 BC 的组合。我会用 B 的思路解决“工具跑不起来”的问题再把扫描结果接入 CI 门禁和 pre-commit让硬编码字符串在合并前就被拦下。这个方案的好处是工具本体完全保持上游同步适配逻辑全在自己仓库里出问题好排查、好回滚。2.2 封装层到底做什么我写的封装脚本做三件事一是自动识别当前是不是鸿蒙 Flutter 工程检测存在 DevEco 工程标志比如 hvigorfile 文件或者 AppScope 目录二是按鸿蒙工程的真实结构拼接扫描参数自动排除 oh_modules、build、.hvigot 等目录三是针对 .ets 文件做一次补充扫描用我自己维护的一个轻量规则集把 ArkTS 里明显的硬编码字符串也捞出来。在实际执行时这些功能全都做在一个 wrapper.sh 里。第一次跑的时候可能会遇到“工具找不到项目根目录”“排除规则不生效”这类小问题我后面会逐个展开讲。3. 鸿蒙 Flutter 工程的接入实操3.1 环境准备版本对齐是第一道坎做鸿蒙 Flutter 开发版本问题比别的平台更敏感。OpenHarmony 社区维护的 flutter_flutter 分支和华为 DevEco Studio 内置的 Flutter SDK 都有各自的版本节奏。适配 string_literal_finder 时我建议先确认三件事第一你的 Dart/Flutter SDK 版本是否能正常跑 dart run 命令第二工程 pubspec.yaml 的 environment 是否声明了合适的 sdk 约束第三是否能正常拉取三方依赖在网络受限环境下需要配置可用的 pub 镜像源。我实际踩过一次低版本 Dart 的坑某台 CI 机器用的 Flutter SDK 是旧分支Dart 版本不够新直接运行 string_literal_finder 时分析器初始化报错。后来统一了 CI 机器上的 Flutter 版本问题就消失了。版本统一是这类工具稳定跑起来的前提建议直接把 SDK 版本写入环境管理文件避免每台机器行为不一致。3.2 在 pubspec.yaml 里接入依赖接入方式很简单在 dev_dependencies 里加一行dev_dependencies: string_literal_finder: ^1.0.0然后执行flutter pub get如果团队有条件建议固定精确版本而不是用脱字符。因为我见过一次三方库小版本更新后报告格式变了把 CI 解析脚本弄崩了。固定版本能减少这类意外。锁定后把 pubspec.lock 提交进仓库确保所有人跑的是同一套依赖树。3.3 首次运行与基线验证依赖装好后先直接跑一把看看默认行为dart run string_literal_finder默认情况下它扫的是当前目录下的 Dart 代码。在鸿蒙工程里你会立刻发现一堆问题oh_modules 目录被扫描了、build 目录被扫描了大量依赖包内部的字符串也被当成问题报出来。这不是工具坏了是它的“全局扫描”策略没有排除鸿蒙工程的“周边”目录。于是封装层的必要性就体现出来了。先配置最小可用的排除规则把无关内容过滤掉dart run string_literal_finder \ --exclude oh_modules/** \ --exclude build/** \ --exclude .dart_tool/** \ --exclude **/*.g.dart \ --exclude **/*.freezed.dart \ --exclude **/*.gen.dart这里的思路是扫描结果里只保留“人写的源码”。自动生成代码虽然可能包含字符串字面量但那是代码生成器决定的不该让它们在治理报告里掺和否则团队成员会对报告失去信任。3.4 扫描范围怎么对齐鸿蒙目录结构在鸿蒙 Flutter 工程里Dart 源码的存放位置和标准 Flutter 工程不完全一样。常见的情况是 Flutter 模块代码在独立目录里和 entry 模块平级。如果直接扫全仓库会有大量无效文件如果只扫 lib/可能漏掉实际存在于其他目录下的 Dart 代码。我的做法是在封装脚本里通过查找 pubspec.yaml 所在目录来定位 Flutter 模块根目录再从这个根目录出发拼接 --path 参数。这么做的好处是无论工程在 DevEco Studio 里怎么组织只要 pubspec.yaml 定位准确扫描范围就不会跑偏。FLUTTER_MODULE_DIR$(find $PROJECT_ROOT -name pubspec.yaml -exec dirname {} \; | head -n 1) dart run string_literal_finder \ --path $FLUTTER_MODULE_DIR/lib \ --exclude oh_modules/** \ --exclude build/** \ --exclude .dart_tool/** \ --exclude **/*.g.dart3.5 对 ArkTS 文件的补充扫描鸿蒙混编场景绕不开 .ets 文件。string_literal_finder 作为一个 Dart 生态工具不会主动适配 ArkTS。但硬编码字符串治理如果漏掉 .ets等于漏掉了半个鸿蒙界面层。我的做法是写一个轻量 Python 脚本用带状态机的遍历方式提取 .ets 文件里的字符串常量再通过一个规则集过滤掉明显无害的内容。这个方案的好处是不用改 Dart 工具本身ArkTS 文件单独走一条扫描线结果汇总到同一份报告里。要说明的是ArkTS 扫描的规则集和 Dart 侧不完全一致。Dart 侧可以依赖语法分析做精确判断而我的 Python 脚本本质是“更聪明的正则”所以要给规则留出足够空间宁可多报、不可漏报。团队评审报告时对 .ets 的误报容忍度要放高一点先把漏报堵死。4. 扫描规则与国际化规范落地4.1 什么算必须拦截的硬编码字符串不是所有字符串都需要国际化。我整理的判定标准有三条第一出现在 UI 上直接展示的文案必须拦截。比如按钮文字、提示信息、错误信息、商品名称的回退文案。第二可被用户感知的内容键必须拦截。比如无障碍描述、语义标签、分享文案。第三纯技术性的字符串不属于硬编码文案。比如网络请求路径、资源 key、正则表达式、格式化占位符、纯数字、空字符串、URL、包名等。string_literal_finder 的 ignore 规则可以处理第三类。开发者可以在配置文件里声明这些技术字符串的匹配模式工具在报告时会自动过滤。维护好 ignore 规则是降低噪音、让团队愿意用这个工具的关键。配置示例ignore: - ^$ # 空字符串 - ^[0-9.,]$ # 纯数字 - ^https?:// # URL - ^[a-zA-Z0-9_/.]$ # 文件路径、包名等技术字符串 - ^%[a-zA-Z]?[dfs]$ # 格式化占位符4.2 鸿蒙工程里必须追加的排除与忽略项鸿蒙工程多出来的自动生成目录前面已经提过。这里再补充一项容易忽略的resources 目录下的 json、配置文件以及多语言文案表本身不应该被扫描。它们不是代码字符串是资源文件。如果工具一股脑扫进去报告会变得一团乱。所以在 exclude 规则里我建议把以下目录全部加入- **/resources/** - **/AppScope/** - **/oh_modules/** - **/.hvigot/** - **/build/** - **/*.json - **/*.arb这里核心原则是只治理“写在代码逻辑里的硬编码”不治理“声明在资源文件里的内容”。4.3 Flutter 与鸿蒙原生资源两侧的国际化联动工具只能发现问题真正让问题消失还要靠国际化方案。在鸿蒙混编场景下我推荐的落地方式是分工明确Flutter 模块内的文案统一走 Flutter 的 intl 机制文案定义在 .arb 文件里Dart 代码中用 AppLocalizations.of(context) 取文案。ArkTS 页面里的文案统一走鸿蒙的资源体系在 resources/base/element/string.json 里定义键值代码中用 $r(app.string.xxx) 取引用。两侧的字符串 key 尽量保持一致的命名习惯。比如登录按钮Flutter 侧是 loginButtonArkTS 侧也是 loginButton。这样产品审一圈文案时不用在两套体系之间来回切换能省下不少沟通成本。4.4 让扫描结果成为国际化的“任务清单”string_literal_finder 的报告直接落到一个 violations.json 文件里我把这个文件导入到一个简单的治理面板中每个扫描结果都会转成一个待办事项指定给对应模块负责人。治理面板不追求复杂能按目录筛选、按负责人筛选、标记状态就够了。我试过让这个流程全自动每次 CI 跑完violations.json 自动上传并更新面板数据。团队每周开一次治理例会直接看面板上的增量数据。三个月后新增硬编码基本清零存量在稳步下降。5. 扫描门禁把规范焊死在提交之前5.1 pre-commit 钩子怎么写工具跑得再好如果靠人自觉去执行迟早会失效。我建议把扫描接到 git pre-commit 钩子里。开发者在本地提交代码时如果新增的代码里出现硬编码字符串提交直接被拦住。一个可用的 .git/hooks/pre-commit 脚本核心逻辑如下#!/bin/sh STAGED_DART_FILES$(git diff --cached --name-only --diff-filterACM -- *.dart) if [ -z $STAGED_DART_FILES ]; then exit 0 fi dart run string_literal_finder \ --path $FLUTTER_MODULE_DIR/lib \ --exclude oh_modules/** \ --exclude build/** \ --exclude .dart_tool/** \ --exclude **/*.g.dart if [ $? -ne 0 ]; then echo 提交失败检测到硬编码字符串请先完成国际化处理 exit 1 fi exit 0这里有个经验之谈别一上来就把全量报告作为门禁依据。老项目存量问题很多全量拦截会让团队寸步难行最终大家会绕过钩子。我建议前两个月只对“新增代码”做拦截或者只拦截 git diff 中新增的行。等存量清理差不多了再逐步收紧。5.2 CI 流水线里的增量扫描线上 CI 环节我跑的是全量扫描加增量对比。全量扫描产生一份当前基线报告增量对比则是把当前分支的扫描结果和主干基线做 diff。只有 diff 出的新增违规才会阻断流水线。这样设计的原因很现实全量报告用于度量整体治理进度但不能直接阻断开发新增违规才是“这周新写的债”必须当场拦住。如果你的 CI 系统是 Jenkins写个脚本调两个命令即可如果是 GitLab CI 或 GitHub Actions思路是一样的。5.3 扫描超时的处理鸿蒙 Flutter 工程比传统 Flutter 工程多了不少自动生成文件首次全量扫描如果排除规则没写好速度会慢到让人怀疑人生。我遇到过扫描 20 分钟还没结束的情况。优化方案很简单把 oh_modules、build 这类大目录全部 exclude 之后实际扫描范围小了一个数量级速度降到 1 分钟以内。如果工程实在太大可以拆成模块粒度分别扫描再聚合报告。6. 常见问题与排查技巧实录6.1 报错无法初始化 Dart analysis context现象是运行 string_literal_finder 时输出类似“Failed to initialize analysis context”的错误。原因通常是 Flutter/Dart SDK 与工程的 sdk 约束不匹配或者 pub 依赖没有正确拉取。排查顺序先执行 flutter --version 确认版本再确认 pubspec.lock 是否存在最后看是否能用 flutter analyze 正常分析当前工程。如果 flutter analyze 本身能跑通那 string_literal_finder 大概率也能跑通如果连 flutter analyze 都报错问题就在更基础的 SDK 环境上。6.2 oh_modules 里的字符串被大量上报一开始扫描报告里的违规数成千上万一看全是 oh_modules 里三方库源码的字符串。这时候不是治理失效是 exclude 规则没生效。检查一下排除规则的正则写法。在 string_literal_finder 里排除路径支持 glob 风格匹配用 oh_modules/** 比用绝对路径更稳妥。如果规则放到了 ignore字符串内容白名单而不是 exclude路径排除也会出现“路径没排掉”的错觉这两个概念的配置位置要区分清楚。6.3 模板字符串和插值字符串被漏报string_literal_finder 对字符串插值是能识别的但如果你自己写了补充扫描脚本处理 .ets 或纯文本场景漏报就很容易发生。比如 Hello $name 这种动态拼接简单正则很容易漏。我的建议是凡是动态拼接的复杂字符串先不追求一次扫全把静态字符串常量先治理干净再慢慢完善动态场景的规则。6.4 大量旧项目存量违规怎么推这是绝大多数团队真正会遇到的问题。一次扫描出来几千条违规直接推给开发改不现实不管吧扫描形同虚设。我的做法是三个字分梯队。第一梯队拦截新增。从推行第一周开始新增代码的硬编码必须清零。第二梯队清理高危。UI 页面直接展示的文案优先处理这部分用户能感知上线翻译或文案修改时顺便改掉。第三梯队技术字符串。比如日志、调试信息、测试代码里的字符串逐批加入 ignore 规则或分批处理。我实践下来第一步比第二步第三步重要得多。如果新增一直被拦住存量即使几周没变化治理的“水位线”也是稳的怕的是新增和存量混在一起团队疲于应付最后工具被废弃。6.5 报告解析脚本被格式变化搞挂我之前用简单字符串匹配解析命令行输出后来 CI 报告老是坏。排查半天发现是三方库版本更新后输出格式里多了一个字段。从此改用输出 JSON 报告再解析。具体做法是在扫描命令里加上报告输出参数生成结构化结果后续所有统计和门禁都基于 JSON 做避免字符串解析的脆弱性。7. 这套方案跑起来之后的感受在实际项目中跑通这套鸿蒙化适配后我的体会是工具迁移这件事最难的从来不是把工具编译过、跑起来而是让它在新的工程生态里找到自己的位置。string_literal_finder 的核心价值在鸿蒙场景下没有变——它依然是硬编码字符串的探照灯但为了让探照灯真正照亮正确的地方我在外面加了一整圈鸿蒙工程适配层。再分享一个小技巧在扫描规则上可以把“TODO 国际化”的字符串也作为一个单独规则标记出来让开发在写代码时就能留下待办信号产品翻文案时也能顺着 TODO 找到所有要补的位置。这个不起眼的改动反而让团队对新工具的接受度高了很多。如果你的鸿蒙 Flutter 工程还在为“代码里的中文写死”发愁别急着买治理平台先把 string_literal_finder 按这篇文章的思路接进来跑上一周的数据再说。工具加流程加一点耐心硬编码字符串这个问题是真的能被治理干净的。
阅读完成 · 觉得有帮助?
咨询建站