fargon2 鸿蒙 Flutter 插件适配实测,ArkTS 完整实现 Argon2 算法,Dart 层零改动,对齐 Android/iOS 返回 Base64
开发工具: 华为云码道
DevEco Studio:
26.0.0(26.0.0.851)原始仓库: iktakahiro/fargon2(GitHub)
本文配套仓库: oh-flutter/fargon2(AtomGit)
fargon2 把 Argon2 口令哈希封装成一个跨平台调用:Dart 侧只需要给出模式与参数,Android 通过 argon2kt 调用参考 C 实现,iOS 通过 CatCrypto 调用系统外的 Argon2 实现。本文以 fargon2 1.0.1 为例,介绍源码准备、OHOS 平台实现、Example 宿主工程配置和真机运行验证。

这个库的适配比一般插件重得多:OpenHarmony 的系统密码学框架没有 Argon2,因此适配不是「调一个系统 API 再包一层」,而是在 ArkTS 侧把 BLAKE2b 与 Argon2d / Argon2i / Argon2id 完整实现出来。Dart 层保持零改动,通道名、方法名、入参键名与返回值类型与 Android / iOS 完全一致。文中的代码以提交 6be4e42030b88bae197b4726fad4885de32fda77 为参考,对应 TAG 1.0.1-ohos-1.0.0-beta.1;文中所有截图均为真机实拍。

一、插件简介与适配目标
Argon2 是 2015 年密码哈希竞赛(PHC)的获胜算法,也是目前口令存储场景的推荐选择。它把「时间代价」「内存代价」「并行度」三个维度都做成参数,让攻击者既不能靠算力硬堆、也不能靠显卡并行绕开。三个变体的差异在于索引计算如何依赖内存内容:
| 变体 | 索引计算 | 适用场景 |
|---|---|---|
argon2d | 依赖内存块内容 | 抗 GPU 最强,但存在侧信道风险,不推荐用于口令 |
argon2i | 不依赖内存内容 | 抗侧信道,适合不可信执行环境 |
argon2id | 前半段 argon2i、后半段 argon2d | 兼顾两者,当前推荐默认 |
fargon2 对外只暴露一个方法:
Fargon2(mode: Fargon2Mode.argon2id).hash(
passphrase: String,
salt: String,
hashLength: int = 32,
iterations: int = 3,
parallelism: int = 2,
memoryKibibytes: int = 65536,
) -> Future<String>
返回的是 raw hash 字节的 Base64 字符串(不是 PHC 那种 $argon2id$v=19$m=... 的编码串),Dart 层还会把换行去掉。这个返回值契约是适配时必须逐字节对齐的关键:Android 与 iOS 都返回 Base64,OHOS 侧如果返回 Hex 或 PHC 串,业务侧的解码代码就会跨平台不一致。
上游的原生实现依赖较重:android/ 使用 argon2kt(JNI 调用参考 C 实现),ios/ 使用 CatCrypto(Swift 侧内置 Argon2)。而 OpenHarmony 的系统密码学框架 @ohos.security.cryptoFramework 只提供 SHA / MD5 / SM3 / HMAC / PBKDF2 / SCRYPT / HKDF / X963KDF——不包含 Argon2。所以本次适配要做的是:
- 在
pubspec.yaml里声明ohos平台,让工具链生成注册代码; - 在 ArkTS 侧实现 BLAKE2b 与 Argon2 三种变体,让同名通道
fargon2的同名方法hash返回与 Android / iOS 完全一致的 Base64; - 生成可编译、可运行、可验证的
example/ohos宿主工程,在真机上跑通三种模式、参数调整、重复计算一致性与性能边界。
二、环境准备
环境搭建参考社区文档:Flutter OH 开发环境搭建,完成 Flutter OH SDK 安装、环境变量和 DevEco Studio 配置。
完成后,在宿主机终端执行以下命令,确认当前选中的是支持 OHOS 的 Flutter 工具链,并能发现目标设备:
flutter --version
flutter doctor -v
hdc list targets


工程使用的工具链和 SDK 配置如下:
| 项目 | 版本或配置 | 用途 |
|---|---|---|
| Flutter OHOS SDK | 3.47.7-ohos-1.0.0 | Flutter 编译与 OHOS 平台工具链 |
| Flutter 分支 | oh-3.47-dev | 对应开发分支(framework revision 4b5928c43f) |
| Dart SDK | 3.13.3 | Dart 语言与包管理环境 |
| DevEco Studio | 26.0.0(26.0.0.851) | OHOS 应用工程开发套件(build 261.23567.138.36.2600851) |
compatibleSdkVersion | 6.1.1(24) | 当前工程声明的最低兼容版本 |
targetSdkVersion | 6.1.1(24) | 应用面向的行为版本 |
| 测试设备 | TLR-AL00(nova 14),OpenHarmony-6.1.1.120(API 24),arm64-v8a | 真机验证环境 |
| 库版本 | 1.0.1 | pubspec.yaml 中的包版本 |
| 原生语言 | ArkTS | ohos/ 模块实现语言 |
| 插件产物 | HAR(ohos/) | 经 GeneratedPluginRegistrant.ets 自动注册 |
2.1 开发套件版本与工程中的 SDK 版本配置
6.1.1(24) 和工程配置涉及 OpenHarmony 版本号体系。在本文工程中,相关版本的含义与配置方式如下:
6.1.1(24)采用「版本(API)」写法,表示 OpenHarmony6.1.1版本、对应 API 24;compatibleSdkVersion与targetSdkVersion均为6.1.1(24):声明最低兼容 API 24,并以 API 24 为行为目标;- 本机 DevEco Studio 为
26.0.0(26.0.0.851),自带 SDK 为 API 26,高于工程声明值;编译按本机 SDK 执行、安装兼容性按工程声明判断,两者并不冲突; - 本次构建与真机验证都使用 DevEco Studio 26.0.0(26.0.0.851) 自带的
hvigorw与 JBR。

判断依据是设备的 hdc shell param get const.ohos.apiversion:本次测试机返回 24,const.ohos.fullname 返回 OpenHarmony-6.1.1.120,与工程声明一致,安装与运行的版本门槛都满足。
三、从源码仓库开始准备适配工程
3.1 将上游源码同步到 AtomGit
适配已有第三方库时,先从 Pub 包信息或项目 README 确认上游地址、包名、版本和许可证。同步仓库时保留源码、许可证和提交历史,方便后续跟踪上游更新。
在 AtomGit 网页的新建仓库流程中,找到从已有仓库导入的入口,填写上游 Git 地址,选择自己有写权限的组织或个人空间,设置目标仓库名称后执行导入。完成后检查目标分支、pubspec.yaml、LICENSE 和提交历史是否完整,并记录适配起点的提交号。如果上游本身托管在 AtomGit,也可以通过 Fork 获得自己的工作仓库。
本例的上游为 iktakahiro/fargon2,包名 fargon2,pubspec.yaml 中的版本为 1.0.1,许可证为 MIT(LICENSE 开头是 MIT License / Copyright (c) 2021 Takahiro Ikeuchi)。适配起点的提交是 2f9f0e8(Merge branch 'release/1.0.1',上游最后一个发布相关提交,时间为 2023-05-08)。
仓库名与 Dart 包名一致,都是 fargon2,因此同步到 AtomGit 时保持原名即可,不必加 fluttertpc_ 前缀。
fargon2 现可从 AtomGit 获取。下面使用 AtomGit 地址拉取代码;需要提交修改时,使用自己有写权限的仓库或 Fork。
3.2 将代码拉取到宿主机
在安装了 Flutter OH 和 DevEco Studio 的开发电脑上打开终端,进入准备存放项目的目录,执行:
git clone https://atomgit.com/oh-flutter/fargon2.git
cd fargon2
pwd
ls
git remote -v
git status --short --branch
git rev-parse HEAD
git clone 会创建 fargon2/ 目录;cd 后的位置就是下文所说的仓库根目录,这里应能看到 pubspec.yaml、lib/、ohos/、example/,以及上游保留的 android/、ios/ 和 docs/。
需要使用与本文相同的代码版本时,在没有未提交修改的仓库中切换到以下提交:
git switch --detach 6be4e42030b88bae197b4726fad4885de32fda77
适配其他插件时,使用该插件对应版本的 tag 或 commit 作为分支起点。

图 1:在宿主机终端输入 AtomGit 仓库拉取命令。
3.3 在仓库根目录确认分支与版本标记
本库没有为 OHOS 适配单独开特性分支,适配提交直接落在 main 上,版本通过 TAG 标记。TAG 命名规则为 原库版本-ohos-版本号-beta.x,本库使用 1.0.1-ohos-1.0.0-beta.1。在 fargon2/ 根目录执行:
git branch --show-current
git tag --list
git log --oneline -3
git status --short --branch
git branch --show-current 用于确认当前所在分支,git tag --list 用于确认版本标记是否已推送。向接收仓库提交时按其对分支与 TAG 的要求执行。

图 2:在 fargon2 仓库根目录确认分支与 TAG。
图 2 里 git tag --list 同时列出了上游的 v1.0.0、v1.0.1 与本次的 1.0.1-ohos-1.0.0-beta.1,三类标记并存是正常现象:前两者是上游的历史版本,后者是适配版本。需要注意当前 origin 仍指向上游 GitHub,适配提交只在本地 main(## main...origin/main [ahead 1]),推送目标要改成 oh-flutter/fargon2 后再执行推送(做法见 6.3)。
3.4 补全 ohos 平台与 example 宿主工程
上游已经按插件形式声明了 android 与 ios 平台,只是缺少 ohos。补全方式是在仓库根目录执行插件模板生成,让它同时产出 ohos/ 模块与 example/ohos/ 宿主工程:
flutter create . --template=plugin --platforms=ohos --org dev.iktakahiro
- 参数
--template=plugin表示按插件模板生成;对已经有lib/、example/的仓库,它只补缺的目录,不覆盖已有实现; --platforms=ohos让它只生成 OHOS 相关文件;--org dev.iktakahiro决定宿主工程的bundleName,本次生成的是dev.iktakahiro.fargon2_example,与上游 Android 侧的dev.iktakahiro.fargon2包名前缀保持一致;- 最后的
.表示在当前插件目录补全工程,不是另建一层fargon2/。
该命令生成 OHOS 平台脚手架。生成后务必用 diff 检查:模板会连带注入与 OHOS 无关的内容,例如 federated 骨架(lib/*_method_channel.dart、lib/*_platform_interface.dart、lib/*_web.dart)、.kts 构建脚本、*.iml、local.properties,还可能改写 analysis_options.yaml。本次适配保留了已有 API、其他平台注册项及依赖配置,只保留 ohos/ 与 example/ohos/ 相关产物,并在 analysis_options.yaml 中补了 analyzer.exclude(build/**、android/**、ios/**),避免 analyze 扫到生成物与原生工程。

图 3:在仓库根目录执行插件模板生成,并确认 pubspec.yaml 新增了 ohos 声明。
pubspec.yaml 的改动很小,只有两行:
ohos:
pluginClass: Fargon2Plugin
pluginClass 必须与 ArkTS 侧类的 getUniqueClassName() 返回值一致,否则 Flutter 引擎在注册阶段会找不到插件类。
3.5 适配后的项目目录
fargon2/
├── lib/
│ └── fargon2.dart
├── ohos/
│ ├── index.ets
│ ├── oh-package.json5
│ ├── hvigorfile.ts
│ └── src/main/
│ ├── module.json5
│ └── ets/components/plugin/
│ ├── Fargon2Plugin.ets
│ ├── Argon2.ets
│ ├── Blake2b.ets
│ └── Bytes.ets
├── example/
│ ├── lib/main.dart
│ └── ohos/
├── test/
│ └── fargon2_test.dart
├── docs/ohos/
├── README.OpenHarmony_CN.md
├── CHANGELOG.OpenHarmony.md
└── pubspec.yaml
项目根目录如下,其中包含 ohos/、example/,以及 OpenHarmony 中英文说明和变更记录文件:

图 4:适配后的 fargon2 仓库目录结构。
各新增文件的职责:
| 文件 | 职责 |
|---|---|
ohos/index.ets | 导出 Fargon2Plugin,作为 HAR 的入口 |
ohos/oh-package.json5 | HAR 名称 / 版本 / 许可证(name: fargon2、license: MIT) |
ohos/src/main/module.json5 | 模块声明(type: har,deviceTypes: default / tablet) |
ohos/.../Fargon2Plugin.ets | 通道注册、参数校验、Base64 返回、hilog(209 行) |
ohos/.../Argon2.ets | Argon2d / i / id 核心:H0、填充阶段、索引计算、变长哈希 H’(486 行) |
ohos/.../Blake2b.ets | BLAKE2b,Argon2 的 H0 与 H’ 依赖它(218 行) |
ohos/.../Bytes.ets | UTF-8 编码、Base64、小端整数读写(95 行) |
example/ohos/ | 宿主工程:EntryAbility 加载 Flutter 引擎,GeneratedPluginRegistrant 注册插件 |
README.OpenHarmony_CN.md | 安装方式、TAG 对应表、约束与 API 说明 |
CHANGELOG.OpenHarmony.md | 适配版本变更记录与已知限制 |
四、Dart 实现分析
lib/fargon2.dart 全文 67 行,是整个库唯一的 Dart 文件。它不依赖任何平台判断,也不做能力探测——这一点决定了本次适配对 Dart 层的零改动。
结构非常简单:
Fargon2Mode枚举:argon2i/argon2d/argon2id,配一个Fargon2ModeString扩展把枚举转成小写字符串;Fargon2类:持有mode,静态常量MethodChannel('fargon2');hash():组装参数、校验 salt 长度、invokeMethod('hash', params)、去掉返回值里的换行;Fargon2ArgumentError:Dart 层的参数异常类型。
4.1 跨端架构与调用时序
三端共用同一条通道契约,差异只在「谁来算」:Android 与 iOS 交给各自的原生库,OHOS 交给 ArkTS 实现。
4.1.1 一次 hash 的完整时序
这条时序里有三个必须与 Android / iOS 完全一致的点:
- password 与 salt 的字节化:三端都按 UTF-8 编码。Android 用
String.toByteArray()(默认 UTF-8),iOS 用 CatCrypto 的 UTF-8 转换,ArkTS 侧用Bytes.ets里的utf8Encode手写实现(不用TextEncoder,避免不同引擎对孤立代理项的处理差异); - H0 里写进参数块的数字:
p、dkLen、m、t、version、type全部按小端 32 位写入,顺序错一个字段,结果就完全不同; - 最终输出的编码:只做 Base64,不加 PHC 前缀、不做十六进制。
4.2 参数校验分两层
Dart 层只校验一件事:salt.length < 8 时直接抛 Fargon2ArgumentError,请求根本不会发出。其余参数(hashLength、iterations、parallelism、memoryKibibytes)在 Dart 层不做校验,交给原生侧。
ArkTS 侧做了防御性校验,覆盖 Dart 层没管的部分:
| 校验项 | 规则 | 错误码 |
|---|---|---|
| 四个数值参数 | 必须为整数(Number.isInteger) | FARGON2_BAD_ARGS |
salt | 长度 ≥ 8 | FARGON2_BAD_ARGS |
hash_length | ≥ 4 | FARGON2_BAD_ARGS |
iterations | ≥ 1 | FARGON2_BAD_ARGS |
parallelism | ≥ 1 | FARGON2_BAD_ARGS |
memory_kibibytes | ≥ 8 × parallelism | FARGON2_BAD_ARGS |
memory_kibibytes | ≤ 1 GiB(MAX_MEMORY_KIB) | FARGON2_BAD_ARGS |
| 计算过程异常 | try/catch 捕获 | FARGON2_ERROR |
memory_kibibytes 的上限是本适配新增的保护:纯 ArkTS 实现在 64 MiB 这一档已经会出问题(见 8.6 与 9.7),没有上限时一个错误参数就可能把进程拖死。上限取 1 GiB 是为了与参考实现的推荐区间对齐,同时给出明确的错误而不是无响应。
4.3 公开 API
| 名称 | 类型 | 入参 | 返回 | OHOS 支持 |
|---|---|---|---|---|
Fargon2 | 构造函数 | Fargon2Mode mode | Fargon2 | yes |
Fargon2.hash | 方法 | passphrase、salt、hashLength=32、iterations=3、parallelism=2、memoryKibibytes=65536 | Future<String>(Base64) | yes |
Fargon2Mode | 枚举 | — | argon2i / argon2d / argon2id | yes |
Fargon2ArgumentError | 异常 | message | — | yes(Dart 层抛出) |
通道契约:
| 项 | 值 |
|---|---|
| 通道名 | fargon2 |
| 方法名 | hash |
| 入参键 | mode、password、salt、hash_length、iterations、parallelism、memory_kibibytes |
| 返回 | Base64(raw hash 字节) |
| 错误码 | FARGON2_BAD_ARGS、FARGON2_ERROR |
注意入参键名是 password 而不是 passphrase:Dart 层的参数叫 passphrase,但发给原生侧的键是 password,这是上游既有约定,适配时按原样保留。
4.4 为什么 Dart 层可以零改动
因为 fargon2.dart 里没有任何平台分支:
- 没有
Platform.isAndroid/Platform.isIOS之类的判断; - 没有
defaultTargetPlatform或kIsWeb分支; - 没有能力探测(不询问平台「你支持 Argon2 吗」)。
所以只要原生侧实现了同名通道、同名方法、同键名入参、同类型返回值,Dart 层不需要加 ohos 分支就能直接工作。这与需要判断平台的库(例如 app_local 那类带 Platform.isXxx 的)形成对比:那类库必须补一个 isOhos 分支,否则会静默走错路径。
五、ArkTS 平台实现
5.1 插件注册
ohos/index.ets 只有两行有效代码:
import Fargon2Plugin from './src/main/ets/components/plugin/Fargon2Plugin';
export default Fargon2Plugin;
插件类实现 FlutterPlugin 与 MethodCallHandler:
export default class Fargon2Plugin implements FlutterPlugin, MethodCallHandler {
private channel: MethodChannel | null = null;
private argon2: Argon2 = new Argon2();
getUniqueClassName(): string {
return 'Fargon2Plugin';
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.channel = new MethodChannel(binding.getBinaryMessenger(), CHANNEL_NAME);
this.channel.setMethodCallHandler(this);
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
if (this.channel != null) {
this.channel.setMethodCallHandler(null);
this.channel = null;
}
}
}
CHANNEL_NAME 常量写死为 'fargon2',并带注释指回 Dart 侧的 const MethodChannel('fargon2')——这类「两处字符串必须一致却不能靠编译器约束」的约定,写注释是最省事的维护方式。
onDetachedFromEngine 里先把 handler 置空再置空 channel,避免引擎销毁后插件实例仍被通道持有:Flutter OH 的插件对象生命周期与引擎绑定,漏掉这一步在反复热重启时容易积累引用。
example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets 由工具链生成,内容是:
const TAG = "GeneratedPluginRegistrant";
export class GeneratedPluginRegistrant {
static registerWith(flutterEngine: FlutterEngine) {
try {
flutterEngine.getPlugins()?.add(new Fargon2Plugin());
} catch (e) {
Log.e(TAG, "Tried to register plugins with FlutterEngine (...) failed.");
Log.e(TAG, "Received exception while registering", e);
}
}
}
它引用的是 from 'fargon2',也就是 ohos/oh-package.json5 中声明的 HAR 名称。改 HAR 名称(例如加 fluttertpc_ 前缀)时必须同步 pubspec.yaml 的 pluginClass 与这里的导入名,否则注册阶段就会失败。
5.2 方法分发与参数读取
onMethodCall(call: MethodCall, result: MethodResult): void {
try {
if (call.method === METHOD_HASH) {
this.handleHash(call, result);
} else {
result.notImplemented();
}
} catch (err) {
result.error(ERR_CODE, 'Failed to handle ' + call.method + ': ' + error.message, '');
}
}
未实现的方法返回 notImplemented(),对应 Dart 侧的 MissingPluginException——这是 Flutter 的跨平台约定,不要改成 result.success(null),否则业务侧会把「方法不存在」当成「返回空值」。
handleHash 先一次性把所有参数取出来,再逐项校验:
mode = call.argument('mode') as string;
password = call.argument('password') as string;
salt = call.argument('salt') as string;
hashLength = call.argument('hash_length') as number;
iterations = call.argument('iterations') as number;
parallelism = call.argument('parallelism') as number;
memoryKibibytes = call.argument('memory_kibibytes') as number;
memory_kibibytes 在 Dart 侧默认是 65536,如果业务没传、原生侧又按 0 处理,就会命中「小于 8 × parallelism」的校验。所以这里对每个键都做了显式取值,不做「缺省补默认值」的猜测——补默认值会让「业务传了非法值」和「业务没传值」两种情况的错误信息混在一起。
5.3 为什么是自实现,而不是降级或返回不支持
这是本次适配最关键的决策,三个备选方案都被否掉了:
- 返回
null或抛「不支持」:fargon2的语义就是「给我一个哈希」,返回空值会让业务侧的hash()调用直接崩在后续解码上,或者更糟——静默存了一个空口令哈希。这个库在 OHOS 上就彻底不可用了。 - 降级成 PBKDF2 / SCRYPT:系统给了
PBKDF2与SCRYPT,但它们是不同的算法,产出的哈希与 Android / iOS 不一致。同一份口令在鸿蒙上算出 A、在 Android 上算出 B,服务端校验就会一半失败一半通过。 - 在 ArkTS 侧实现 Argon2:唯一能同时满足「跨端一致」和「功能可用」的方案,代价是性能(见 5.5 与 8.6)。
判断依据是先确认系统确实没有:在 SDK 的 .d.ts 里全量检索 argon 没有任何匹配,cryptoFramework 的 Md / Mac / Kdf 能力列表里也没有 Argon2 相关接口。这个结论写进了 CHANGELOG.OpenHarmony.md 与两份 README,避免后来者重复踩一遍。
5.4 算法实现结构
四个文件的分工是一条直线:Bytes 提供字节工具,Blake2b 提供哈希原语,Argon2 组合出算法,Fargon2Plugin 负责对外。
Bytes.ets(95 行):UTF-8 编码、Base64 编码、小端读写。UTF-8 是手写的,逐码点处理 1~4 字节序列,目的是与 Android 的 toByteArray()、iOS 的 UTF-8 转换在「中文」「emoji」「孤立代理项」这些边界上保持一致。
Blake2b.ets(218 行):Argon2 的 H0(参数块哈希)与 H’(变长输出)都建立在 BLAKE2b 上。实现里保留了 64 位运算的 (low, high) 双 32 位表示,并对 IV 常量、sigma 置换表、G 函数的轮转位数逐项对齐参考实现。
Argon2.ets(486 行):三种变体共用一套填充逻辑,差异集中在索引计算:
| 阶段 | 处理方式 |
|---|---|
| H0 | BLAKE2b-512(p, dkLen, m, t, version, type, 口令长度, 口令, salt 长度, salt, 秘密, 附加数据) |
| 初始块 | 由 H0 派生两个 1 KiB 块,分别作为 B[0]、B[1] |
| 填充 | 按 p 分车道,逐块计算参考块索引——argon2d 用前一块内容决定,argon2i 用计数器伪随机,argon2id 前两轮用 i、之后用 d |
| 收尾 | 各车道末块异或 → 最终块 → H’(dkLen) |
64 位算术全部用 (low, high) 两个 number 表示,而不是 BigInt:填充阶段是热点路径,BigInt 在这类循环里会产生大量装箱与 GC 压力。代价是每个加、乘、移位都要手写进位,这与参考实现的运算顺序必须逐位一致——这也是本次适配花时间最多的部分。
Fargon2Plugin.ets(209 行):通道、校验、Base64 转换、hilog。成功日志里带上了完整参数与耗时,排查性能问题很方便:
hilog.info(DOMAIN, TAG,
'hash ok, mode: %{public}s, dkLen: %{public}d, t: %{public}d, m: %{public}d KiB, p: %{public}d, cost: %{public}d ms',
mode, hashLength, iterations, memoryKibibytes, parallelism, Date.now() - start);
hilog 的参数必须用 %{public}s / %{public}d 明确标注可见性,否则默认按隐私数据处理,在日志里会显示成 <private>,等于没打。
5.5 正确性如何保证
纯自实现的算法最怕「看起来能跑,结果全错」。本适配用了两道校验:
第一道:宿主机与参考实现对拍。 把 Argon2.ets、Blake2b.ets、Bytes.ets 原样复制成 .ts(这三个文件不依赖任何 @ohos.* 接口,因此可以直接用 tsc 编译成 JS 在 Node 下跑),再与 argon2-cffi(PHC 参考 C 实现的 Python 绑定,ARGON2_VERSION=19)逐字节比较。覆盖 23 组参数:三种模式、t=1..4、p=1..4、m=8..65536 KiB、dkLen=4..129(含非 4 的倍数)、中文与 emoji 口令。结果全部一致,报告在 docs/ohos/argon2-vectors.md。
第二道:本次真机输出与本机参考实现复核。 验证时在设备上跑的每一组参数,都在宿主机用 argon2-cffi 重算一遍,逐字节对照:
参数(口令 mypassphrase,盐 mysalt01) | 真机 Base64 | 本机 argon2-cffi | 结论 |
|---|---|---|---|
| argon2id / t=3 / p=2 / m=4096 KiB / dk=32 | GIjNhcX2T7sEYljQaCj+vKOPLb8TkQUEJ+Rkzf2+JLM= | GIjNhcX2T7sEYljQaCj+vKOPLb8TkQUEJ+Rkzf2+JLM= | 一致 |
| argon2id / t=1 / p=1 / m=64 KiB / dk=32 | wAIkIqKRcMNrX/PIOEvGvMYD0Z8GtRXskjQSZ19o3Y= | wAIkIqKRcMNrX/PIOEvGvMYD0Z8GtRXskjQSZ19o3Y= | 一致 |
| argon2i / t=1 / p=1 / m=64 KiB / dk=32 | Xsi5v8gpiW28Db/KtfyRQ/h8QXV5Z54YTjDNeO3473E= | Xsi5v8gpiW28Db/KtfyRQ/h8QXV5Z54YTjDNeO3473E= | 一致 |
第三行的 hex 也逐位相同(5ec8b9bfc829896dbc0dbfcab5fc9143f87c417579679e184e30cd7b4df8ef71),说明不只是 Base64 编码对得上,中间结果也一致。
argon2id 与 argon2i 在同一组参数下产出的值不同(wAIkIqKR… vs Xsi5v8gp…),这从反面证明模式参数确实传到了算法层,而不是被忽略后统一按一种变体算。
5.6 权限
无。
- 插件侧:
ohos/src/main/module.json5只有模块声明(name、type: har、deviceTypes),没有任何requestPermissions; - 宿主侧:
example/ohos/entry/src/main/module.json5的requestPermissions是空数组[]。上游模板原本带了一条ohos.permission.INTERNET,本次适配删掉了——Argon2 计算不联网,留着只会让用户在权限列表里看到一个与功能无关的条目。
六、补全交付文件并提交适配分支
6.1 除代码外还要补全哪些文件
| 文件 | 作用 |
|---|---|
README.OpenHarmony_CN.md | 中文说明:安装方式、TAG 对应表、兼容性、权限、性能、API、实现差异 |
README.OpenHarmony.md | 英文版,内容与中文版对齐 |
CHANGELOG.OpenHarmony.md | 适配版本变更记录,包含已知限制(性能) |
docs/ohos/argon2-vectors.md | 23 组参考向量对拍报告与复现方法 |
docs/ohos/*.jpeg、hilog-ohos.log | 真机证据 |
example/ohos/README.md | 宿主工程的构建、签名、运行说明 |
example/lib/main.dart | 从模板页面重写为可验证的演示页 |
写入文档时有三处需要核对,本文如实记录当前状态:
- 下载地址:两份 README 的
pubspec.yaml示例里,仓库地址写的是https://atomgit.com/CPF-Flutter/fluttertpc_fargon2,而实际配套仓库是https://atomgit.com/oh-flutter/fargon2。组织名与仓库名都不一致,读者按文档复制会拉不到代码,发布前必须统一; - TAG 表的分支列:README 写的是
br_v1.0.1_ohos,实际分支是main。这列目前只是「建议分支」,不影响按 TAG 拉取,但会让人误以为存在这个分支; - 许可证:上游是 MIT,
ohos/oh-package.json5里的license也写的是MIT,这一处是一致的,无需改动。
6.2 提交前检查
example/ohos/build-profile.json5 里当前带着本机签名材料:
"signingConfigs": [
{
"name": "default",
"type": "HarmonyOS",
"material": {
"certpath": "C:\\Users\\PC\\.ohos\\config\\default_ohos_....cer",
"keyAlias": "debugKey",
// ...
这段内容已经随适配提交 6be4e42 进入仓库(可以用 git show 6be4e42:example/ohos/build-profile.json5 查到)。证书路径里带着本机用户名,口令字段也在其中,属于个人环境信息,提交前应当还原为:
"signingConfigs": []
然后用自己的调试签名在本地重新生成(做法见 8.3)。如果仓库已经推送,还要按接收仓库的安全要求决定是否需要清理历史——仅仅在最新提交里删掉,旧提交里依然能查到。
另一个提交前要确认的点是 example/ios/Flutter/ephemeral/:这次 git status 里它显示为未跟踪目录,属于 iOS 构建产物,应确认 .gitignore 覆盖了它,不要误提交。
6.3 提交并推送到 AtomGit
当前仓库的 origin 指向上游 GitHub,无法直接推送适配提交,需要先把远端换成自己的 AtomGit 仓库:
git remote rename origin upstream
git remote add origin https://atomgit.com/oh-flutter/fargon2.git
git remote -v
提交与打 TAG:
git add -A
git commit -m "feat(ohos): implement Argon2 in ArkTS and add OpenHarmony support"
git tag -a 1.0.1-ohos-1.0.0-beta.1 -m "fargon2 1.0.1 OpenHarmony adaptation beta.1"
git push -u origin main
git push origin 1.0.1-ohos-1.0.0-beta.1
保留 upstream 指向 GitHub 的好处是后续可以 git fetch upstream 跟踪上游更新,再 rebase 到自己的 main 上。提交信息建议写清三件事:为什么自实现(系统无 Argon2)、验证到什么程度(参考向量 + 真机)、已知限制(性能),这样后续维护者不必翻代码就能判断改动范围。
七、使用根目录 example 演示接入
7.1 本地适配时使用路径依赖
example/pubspec.yaml 默认就是指向上级的路径依赖,改完库代码直接生效:
dependencies:
flutter:
sdk: flutter
fargon2:
path: ../
在 example/ 目录执行 flutter pub get,再 flutter run -d <device-id> 即可。路径依赖的好处是改 ArkTS 代码后不需要发版;注意改完 ohos/ 下的 .ets 之后要重新构建 HAP,热重载不会重新编译原生侧。
7.2 通过 AtomGit 引入库
业务工程引用适配版时,用 git 依赖加 TAG:
dependencies:
fargon2:
git:
url: https://atomgit.com/oh-flutter/fargon2.git
ref: 1.0.1-ohos-1.0.0-beta.1
执行 flutter pub get 后,Flutter 工具链会读取插件的 pubspec.yaml,识别出 ohos 平台声明,并在宿主工程的 GeneratedPluginRegistrant.ets 里生成注册代码。宿主工程本身仍需要是 OHOS 模板工程(ohos/ 目录、build-profile.json5、entry 模块齐全),插件不会替你生成这些。
7.3 计算口令哈希
import 'package:fargon2/fargon2.dart';
final String hash = await const Fargon2(mode: Fargon2Mode.argon2id).hash(
passphrase: 'mypassphrase',
salt: 'mysalt01', // 至少 8 个字符
hashLength: 32,
iterations: 3,
parallelism: 2,
memoryKibibytes: 4096, // 单位 KiB,鸿蒙上建议 4 MiB~16 MiB
);
// hash 是 raw hash 的 Base64;入库时连同 salt 一起存
三个模式换 mode 即可,参数语义与 Android / iOS 完全一致。
7.4 业务侧的三条注意事项
- salt 必须每个用户独立生成并随哈希一起存。本库不生成 salt,也不做 PHC 串拼接,
salt由业务提供;用固定 salt 等于让相同口令得到相同哈希,彩虹表照旧有效。 - 鸿蒙上的参数要收敛。
memoryKibibytes默认 65536(64 MiB)是给 C 实现准备的,纯 ArkTS 在这个档位不可用(见 8.6)。建议在 OHOS 上显式传 4096~16384;如果业务需要「全平台参数一致」,那就得接受鸿蒙侧偏慢,或者等后续 C++(NAPI)实现。 - 必须
await,不要在计算期间做重 UI 操作。计算发生在平台主线程上,与 Android / iOS 的实现方式一致;计算期间页面无法响应交互,参数越大越明显。
八、验证、构建与鸿蒙设备运行效果
8.1 分别验证库与 example
本仓库包含单元测试 test/fargon2_test.dart,用 setMockMethodCallHandler 把通道打桩成返回固定 Base64,覆盖两个用例:
hash()正常路径返回打桩值,验证 Dart 层的参数组装与返回值透传;salt只有 4 个字符时抛Fargon2ArgumentError,且toString()与预期文案完全一致。
从仓库根目录执行:
flutter pub get
flutter test
flutter analyze
cd example
flutter pub get
flutter analyze
单元测试验证的是 Dart 层,与平台无关;Argon2 的实际计算、参数校验与 Base64 编码只能在鸿蒙设备上确认。example/ 的 analysis_options.yaml 里补了 analyzer.exclude(build/**、android/**、ios/**),否则 flutter analyze 会扫到生成的原生工程。
8.2 确认设备连接
hdc list targets
flutter devices
设备首次连接电脑时,需要在手机端确认调试授权。本次验证使用的设备信息:
hdc shell param get const.ohos.fullname # OpenHarmony-6.1.1.120
hdc shell param get const.product.model # TLR-AL00
hdc shell param get const.ohos.apiversion # 24
hdc shell param get const.product.cpu.abilist # arm64-v8a
8.3 配置签名
真机安装的 HAP 需要有效签名。本仓库当前提交里的 signingConfigs 是别人的本机材料,不能直接用(见 6.2),请替换为自己的调试签名:
- 用 DevEco Studio 打开
example/ohos,不是仓库根目录; - 等待工程 Sync 成功,确认 Project 视图中存在
entry模块; - 打开 File > Project Structure > Signing Configs;
- 勾选 Automatically generate signature;
- 确认设备、应用包名、证书和 Profile 匹配(调试 Profile 与
bundleName: dev.iktakahiro.fargon2_example强绑定); - 再回到终端执行构建或安装。
8.4 构建与安装
在 example/ 目录执行 flutter run -d <device-id> 可以直接构建运行。仅构建 HAP 时:
flutter build hap --debug
Windows 环境下该命令可能因 DevEco Studio 26 的 ohpm.bat 批处理递归缺陷中断(见 9.3),可改用工程内等价的构建命令:
cd example/ohos
export JAVA_HOME="/d/Program Files/Huawei/DevEco Studio/jbr"
export PATH="/d/Program Files/Huawei/DevEco Studio/jbr/bin:$PATH"
hvigorw assembleHap -p product=default -p buildMode=debug --no-daemon
本次验证用的正是后者。第一次执行时在打包阶段失败:
> hvigor ERROR: Failed :entry:default@PackageHap...
> hvigor ERROR: Error Code: 00308018 Unknown Error
spawn java ENOENT
把 DevEco Studio 自带的 JBR 加入 PATH 后重新执行,构建通过:
> hvigor Finished :entry:default@SignHap... after 3 s 949 ms
> hvigor Finished :entry:assembleHap... after 1 ms
> hvigor BUILD SUCCESSFUL in 21 s 774 ms
42 tasks in total: 20 executed, 22 up-to-date
产物位于 example/ohos/entry/build/default/outputs/default/entry-default-signed.hap(约 138 MiB,debug 包带调试符号与完整 flutter_assets)。真机安装:
hdc install -r entry-default-signed.hap
hdc shell aa start -a EntryAbility -b dev.iktakahiro.fargon2_example
pack.info 中的 apiVersion 为 {"compatible": 24, "releaseType": "Release", "target": 24},与测试设备(API 24)一致。
需要说明的是本次重新构建的原因:仓库里原有的 HAP 是 16:29 打的,而 Argon2.ets 的最后修改时间是 16:32——产物比源码旧,不能作为当前代码的验证证据。重新构建后 :entry:default@CompileArkTS 重新执行,产物时间为 18:31,与提交内容对应。
8.5 在设备上测试哈希语义
example 页面按「输入 / 参数 / 结果 / 运行日志」分区,逐项核对:
- 启动应用:默认参数(argon2id / dk=32 / t=3 / p=2 / m=4096 KiB)自动计算一次,首屏头部显示「调用 1 次」,结果区出现 Base64、HEX、耗时与字节数(图 5、图 6 左);
- 核对默认参数的输出:
GIjNhcX2T7sEYljQaCj+vKOPLb8TkQUEJ+Rkzf2+JLM=,耗时 6712 ms——同一组参数在宿主机上算出来的值与它逐字节相同(见 5.5); - 点按 快速预设(
t=1 / p=1 / m=64 KiB)再点 计算哈希:结果变为wAIkIqKR…,耗时降到 131 ms,日志区出现「已载入快速预设」(图 7 左); - 再点一次 计算哈希:结果不变,结果区出现绿色「重复计算一致」标记,日志累计到 8 条(图 6 右);
- 切换模式为 argon2i 后重算:结果变为
Xsi5v8gp…,耗时 125 ms,结果区标签显示「模式 argon2i」(图 7 右); - 点按 载入参考向量(
argon2id / t=3 / p=2 / m=64 MiB / dk=16)再计算:页面在十余秒后退出到桌面,应用被系统终止(图 8 左,结论见 8.6 与 9.7); - 重新启动应用:默认参数自动计算恢复正常,头部重新显示「调用 1 次」(图 8 右)。
前 5 项与第 7 项已在真机完成,第 6 项按「应用被终止」这一事实记录,没有把它写成「通过」。
8.6 鸿蒙设备运行效果
先看输入与参数区。页面的参数控件与 Dart 侧 API 一一对应,内存下拉框下方直接标出 m ≥ 8 × p 的约束:
| 启动后的首屏 | 参数区 |
|---|---|
平台 ohos、通道 fargon2、「调用 1 次」、默认模式 argon2id | 哈希长度 32 字节、迭代 3、并行度 2、内存 4096 KiB(4 MiB),提示「最小值 8 × p = 16 KiB」 |
图 5:示例页的输入与参数区,以及启动后自动完成的第一次计算。
默认参数(argon2id / 4 MiB / t=3 / p=2 / dk=32)的计算结果与完整日志:
| 默认参数计算结果 | 重复计算与日志 |
|---|---|
Base64 GIjNhcX2T7sEYljQaCj+vKOPLb8TkQUEJ+Rkzf2+JLM=,HEX 1888cd85…be24b3,32 B,耗时 6712 ms,模式 argon2id | 同参数再算一次:113 ms,结果不变,出现「重复计算一致」;日志 8 条覆盖 4096 KiB→64 KiB→重复计算 |
图 6:默认参数(argon2id / 4 MiB / t=3 / p=2 / dk=32)的计算结果,以及同参数重复计算的一致性结论。
换到快速预设与 argon2i,可以看到内存代价对耗时的影响,以及模式参数确实生效:
| 快速预设(argon2id / 64 KiB) | 切换到 argon2i |
|---|---|
t=1 / p=1 / m=64 KiB,耗时 131 ms,Base64 wAIkIqKR… | 同样 64 KiB,耗时 125 ms,Base64 Xsi5v8gp…,与 argon2id 结果不同 |
图 7:快速预设(64 KiB)与 argon2i 的结果。两者参数相同、模式不同,输出不同,说明 mode 确实传到了算法层。
64 MiB 参考向量这一档的结果,以及重启后的恢复状态:
| 64 MiB(载入参考向量后计算) | 重新启动应用 |
|---|---|
| 点击计算后约十余秒,应用退出到桌面,进程被系统终止 | aa start 再次启动,默认参数自动计算恢复,头部重新显示「调用 1 次」 |
图 8:64 MiB 参数下应用被系统终止,重新启动后功能恢复正常。
关于第 6 步的终止:设备上 /data/log/faultlog/faultlogger/ 目录对普通 shell 返回 Permission denied,本次没有取到确切的终止原因,因此文中只陈述现象。从代码路径看,两个可疑点都指向同一处设计:Argon2 在平台主线程上同步计算 64 MiB 数据,其间无法响应任何事件;同时 ArkTS 侧对 64 MiB 的 Uint8Array 与中间块会产生多次分配与拷贝,内存峰值远高于 64 MiB 本身。这两者中的任意一个都足以让应用被系统判定为异常。要得到确定结论,需要在 faultlogger 可读的环境(如开放 root 的开发机)复现并读取 appfreeze / cppcrash 记录。
插件侧的日志与界面数据一一对应,TAG 为 Fargon2Plugin(下面按 TAG 裁剪了模块前缀列):
16:29:53.516 FlutterEngineCxnRegistry --> Adding plugin: Fargon2Plugin
16:29:53.516 attached to engine, channel [fargon2] registered
16:29:55.196 onMethodCall: hash
16:30:01.628 hash ok, mode: argon2id, dkLen: 32, t: 3, m: 4096 KiB, p: 2, cost: 6432 ms
第一段是 16:29 那次运行的日志,cost: 6432 ms 与当次界面显示的 6.4 s 对得上;本次 18:32 复跑时界面显示 6712 ms,属于同一档的正常波动(实测区间 6.4~6.7 s)。日志里的 m: 4096 KiB 也直接证明 Dart 侧传下来的 memory_kibibytes 没有被截断或改写。
性能边界可以按内存代价线性外推,实测与推算的对照如下:
| 参数(argon2id,t=3,p=2) | 真机耗时 | 说明 |
|---|---|---|
| m=64 KiB | 131 ms | 快速预设(t=1、p=1) |
| m=4 MiB | 6.4~6.7 s | 默认参数,可用但偏慢 |
| m=16 MiB | 约 25 s(按 4 MiB 线性推算,未实测) | 已接近可接受上限 |
| m=64 MiB | 未完成 | 计算过程中应用被终止(见上) |
这组数据也解释了为什么 README 把「4 MiB~16 MiB」写成建议区间,而不是照搬 Dart 默认的 64 MiB。
九、FAQ:适配过程与使用问题
9.1 Missing SDK components
典型错误如下:
Missing SDK components. SDK path: ...,
missing components: toolchains,ets,js,native,previewer.
这个错误发生在 Hvigor 同步阶段。通常需要检查构建工具使用的 SDK 路径、组件是否完整,以及 Hvigor 与 SDK 的版本是否匹配。
处理顺序:
- 在 DevEco Studio SDK Manager 中确认对应 API 的组件已经下载完整;
- 检查 Flutter 和 DevEco Studio 使用的 SDK 路径是否一致;
- 避免误用不完整的 SDK 目录;
- 确认 SDK 根目录下存在
toolchains、ets、js、native、previewer; - 执行
flutter config --ohos-sdk <正确路径>; - 重新执行
flutter doctor -v和 DevEco Studio Sync。
因为 Sync 和 Compile 首先读取宿主机本地 SDK。手机 API 版本只在部署、安装和运行时参与兼容判断。即使完全不连接手机,本地 SDK 不完整时也会得到相同错误。
当前工程的 compatibleSdkVersion 是 6.1.1(24)(API 24),因此 API 24 设备在安装版本门槛上是满足的。
9.2 spawn java ENOENT
本次实际遇到的错误,出现在 PackageHap 阶段:
> hvigor ERROR: Failed :entry:default@PackageHap...
> hvigor ERROR: Error Code: 00308018 Unknown Error
spawn java ENOENT
hvigorw 是 Node 脚本,打包与签名阶段会再起 java 进程。只设置 JAVA_HOME 不够——它需要能在 PATH 里直接找到 java 可执行文件。DevEco Studio 自带 JBR,把它加进 PATH 即可:
export JAVA_HOME="/d/Program Files/Huawei/DevEco Studio/jbr"
export PATH="/d/Program Files/Huawei/DevEco Studio/jbr/bin:$PATH"
Windows 下路径按 Git Bash 风格写 /d/...;在 CMD / PowerShell 里对应 D:\...\jbr\bin。改完重新执行同一条 hvigorw assembleHap,本次由 45 s 失败变为 21 s 通过。
因为 CompileArkTS、ProcessCompiledResources 这些任务由 hvigor 自己的构建进程完成,直到 PackageHap 与 SignHap 才需要调用外部 java(打包工具与签名工具都是 Java 程序)。所以表现是「代码编译没问题,最后一步突然 ENOENT」。
9.3 flutter build hap 卡在 ohpm install:BATCH RECURSION exceeds STACK limits
Windows + DevEco Studio 26 上执行 flutter build hap 时,可能在依赖安装阶段中断:
[ohpm] BATCH RECURSION exceeds STACK limits
原因是 Flutter 工具链调用 ohpm.bat 时触发了批处理递归。绕过方式是直接用工程内的 hvigorw 构建(8.4 的命令),它与 flutter build hap 的最终产物一致,只是少了 Flutter 侧的前置步骤。
不会。flutter build hap 内部做的事情是「先编译 Dart(生成 kernel_blob.bin 与 flutter_assets),再调 hvigor 打包」。如果 example/ohos 里已经有上次构建的 flutter_assets,而 Dart 代码没有变化,直接 hvigorw assembleHap 得到的产物就是完整的。
但改了 Dart 代码后必须先 flutter build hap(或 flutter run)重新生成 flutter_assets,否则 HAP 里跑的还是旧 Dart 代码。本次验证前源文件的修改时间都早于上一次 Flutter 构建产物,且只重新构建了 ArkTS 侧,因此 Dart 侧无需重编。
9.4 Invalid kernel binary format version (expected 130, found 138)
--release 构建时可能遇到:
Invalid kernel binary format version (expected 130, found 138)
这是 Flutter 引擎产物(gen_snapshot)与当前 Dart SDK 版本不匹配,发生在 AOT 阶段,与插件代码无关。处理方式:
- 改用
--debug构建(本次验证全程使用 debug); - 或执行
flutter precache --ohos(或对应平台的precache)重新拉取引擎产物; - 确认
flutter --version与工程使用的 Flutter SDK 是同一份。
9.5 安装报 code:9568297 older sdk version in the device
install failed due to grant request permissions failed
error: failed to install bundle. code:9568297
这条错误的名字容易误导,实际原因通常是设备的 API 版本低于 HAP 声明的 compatibleSdkVersion。本工程声明 6.1.1(24),API 24 及以上设备可安装;低于 24 的设备需要下调 compatibleSdkVersion(不推荐,会引入未验证的行为差异)。
9.6 安装报 no signature file 或 install version downgrade
no signature file:装的是entry-default-unsigned.hap。选entry-default-signed.hap;install version downgrade:设备上已装的包版本更高。加-r覆盖安装,或先hdc uninstall <bundleName>。
9.7 计算 64 MiB 参数后应用直接退出
本次实测现象:argon2id / t=3 / p=2 / m=64 MiB / dk=16 点击计算后约十余秒,应用回到桌面,进程被系统终止。这是当前纯 ArkTS 实现的能力边界,不是参数校验或通道的问题。
规避方式:
- 把
memoryKibibytes控制在4096~16384; - 确需大内存代价时,在插件内改用 C++(NAPI)实现参考 C 版本 Argon2——本适配没有包含这一部分;
- 若要「快速失败」而不是「卡住被杀」,可以在业务侧先把参数限制在安全区间,再调用
hash()。
按填充阶段的内存块数量线性外推,64 MiB 大约是 4 MiB 的 16 倍,理论耗时在 100 s 量级。但真机上应用在十余秒就被终止,说明先触发的不是「算完」,而是「被打断」——所以 64 MiB 在鸿蒙上的真实结论是「不可用」,而不是「慢一点」。
9.8 默认参数 6.7 秒,正常吗
正常。4 MiB / t=3 / p=2 在 nova 14 上实测 6.4~6.7 s。纯 ArkTS 与 C 实现差了两个数量级:同样的参数在 Android 的 argon2kt 上是几十毫秒量级。这是「用可维护的 ArkTS 换掉 JNI + C 依赖」的代价,属于已知限制,写进了 CHANGELOG.OpenHarmony.md 与两份 README。
如果业务对首次哈希的等待时间敏感,可以在 OHOS 上使用较低的内存代价(4 MiB)并配合一次性的「等待提示」,而不是把参数降到不安全的水平。
9.9 salt 少于 8 个字符时抛出 Fargon2ArgumentError
Fargon2ArgumentError: salt is too short. It must be at least 8 characters.
这个异常来自 Dart 层(lib/fargon2.dart),不会走到通道,所以三端行为一致、也不会有 PlatformException。注意它检查的是字符数而不是字节数:中文 salt 的 UTF-8 字节数更长,但字符数不足 8 时同样会被拒绝。
9.10 抛 MissingPluginException 或 hash 一直不返回
MissingPluginException 表示通道没有注册上,排查顺序:
pubspec.yaml里是否有ohos: pluginClass: Fargon2Plugin;ohos/index.ets是否导出Fargon2Plugin;- 宿主工程的
GeneratedPluginRegistrant.ets是否包含add(new Fargon2Plugin()); - 改完 ArkTS 代码后是否重新构建了 HAP(热重载不会重编原生侧)。
hash 一直不返回则通常是参数过大(见 9.7)——同步计算期间不吐任何中间状态,只能等它结束。
9.11 flutter create 不认识 ohos
Could not find an option named "platforms".
或提示 ohos 不是合法平台,说明当前 Flutter 不是 Flutter OH 版本。执行 flutter --version 确认是 3.47.7-ohos-*,并检查 flutter config --list 里的 SDK 路径指向支持 OHOS 的工具链。
9.12 AtomGit 依赖提示找不到分支或无权限
git 依赖常见的三种报错:
Could not find a file named "pubspec.yaml"——url写错或ref指向不存在的 TAG;fatal: could not read Username—— 仓库是私有的,需要配置凭据或改用带 token 的地址;- 解析到旧代码 ——
ref写的是分支名,分支已前进。发布给业务用时应固定 TAG。
9.13 改了本地库代码,Demo 为什么没变化
两条缓存链路要分别处理:
- Dart 侧:改
lib/*.dart后需要flutter run重新编译(热重载不重编 Dart 的常量与结构变更); - ArkTS 侧:改
ohos/**/*.ets后必须重新构建 HAP 并重装,热重载完全不涉及原生代码。
判断有没有生效,看两处:结果是否符合预期,以及 hilog 里 Fargon2Plugin 的 hash ok 日志是否重新出现。
相关链接
更多推荐



所有评论(0)