Stream and validate native feature loading with fast decoding and bounded SDK batches. Reconcile authoritative department snapshots with rollback-safe metadata and file updates. Bound downloads, isolate duplicate identifiers, and defer unchanged invalid photo versions. Remove unused legacy face APIs and document device measurements. Validation: 42 Flutter tests in an isolated source snapshot; native unit and device checks; all 5,537 registered features verified byte-for-byte, plus real SDK photo searches and metadata checks.
6.4 KiB
人脸库启动装载优化与真机验证
验证日期:2026-10-22。设备:RK3568,Android 11。实际业务库包含 5,537 条人脸,每条特征 2,056 字节,无缺失或损坏特征。
改动
- 原生读取改为
id > ? AND id <= ? ORDER BY id LIMIT 128,只选择装库字段,复用一个数据库连接。记录开始时的数量和最大主键,结束时校验读取数量。 - 标准整数 JSON 数组直接解码到一次分配的
byte[],兼容 Java 有符号字节、Flutter 无符号字节;合法历史数字形式由 JsonReader 兼容解析。拒绝越界、溢出、损坏和多余尾部数据。 - 读取和注册交替执行,注册批次采用 1,000 条,避免先在 Java 中物化整库特征再开始注册。
- 每 150 ms 合并同阶段进度,阶段切换和结束立即发送;load 页显示实际阶段与处理数量。
- 缺失/损坏特征逐条从原图片修复,按页提交修复事务,不再每张图片打开数据库并单独提交。
- SDK 批次失败时清理可能的部分写入后进行二分隔离;清理失败立即中止。非法或重复搜索标识计为失败,禁止截断标识或覆盖其他用户的在库标识。
- 虹软空库清空按幂等成功处理;结束时同时核对读取行数、成功/失败数量、SDK 数量及业务映射数量。数据库错误不返回成功的前缀结果。
- 启动完成整库读取与修复后,收尾仅查询空特征记录,统一处理 NULL、空串、
[]、null;不再把全部轻量档案传回 Dart 筛选。 - 增加
FaceStartupPerf脱敏汇总日志和结果 metrics,区分读库、解析、修复、引擎初始化、注册以及 Dart 激活/清理耗时。
沿用数据库 v15 和原有 JSON 特征存储格式,无数据库迁移。SDK 激活参数校验及原有人脸互斥队列继续复用。
受控 A/B 结果
同一 Debug APK、同一台设备和同一份数据,每组 3 轮,每轮重新初始化虹软引擎。旧算法仅在测试 APK 中保留用于对照;不会把旧实现带入正式包。
| 方案 | 三轮装库耗时 / ms | 中位数 / ms | 注册调用数 |
|---|---|---|---|
| 旧 SELECT *、每条 Gson、每批 100 | 11996 / 11418 / 11387 | 11418 | 56 |
| 优化读取/解析,每批 100 | 3929 / 3841 / 3871 | 3871 | 56 |
| 优化读取/解析,每批 500 | 3590 / 3516 / 3545 | 3545 | 12 |
| 优化读取/解析,每批 1000 | 3560 / 3467 / 3473 | 3473 | 6 |
采用 1,000 条批次后,中位数下降约 69.6%,约为旧算法的 3.29 倍速度。主要收益来自读取及解析;批次调整贡献约 0.4 秒。
最终方案三轮分段耗时:读库约 1.29–1.32 秒,解析约 1.21–1.22 秒,SDK 注册约 0.68–0.70 秒,引擎初始化约 0.25 秒;每轮只有 22 次进度事件。
独立测试不启动 Flutter UI,也不包含旧版逐条进度发送,因此与实际 App 冷启动区分记录。优化前实际 App 日志中,读库/解析约 14.29 秒,从开始查询到装库完成约 17.87 秒。原生 durationMs 不包含 Dart 中的 SDK 激活和收尾清理,完整人脸准备耗时由 startupMs 记录。
正确性与回归
- 每组每轮均成功注册 5,537 条,失败 0 条;核对全部样本标识集合,并逐条从 SDK 取出特征与优化前 Gson 读取的字节比较。
- 从本地四张实际照片生成
ExtractType.RECOGNIZE查询特征,每轮检索均匹配到正确用户。SDK 区分注册特征和查询特征,不能直接使用 REGISTER 特征作查询。 - 原生解析单元测试 3 项通过,包含随机特征及全部字节边界、历史空值、兼容数字形式、畸形/越界/长数字溢出等输入。
- 真机独立内存 SQLite 11 项断言通过:分页边界、坏 JSON 后续行不丢失、标识溢出、修复落库、用户范围、快照上界、读取期间删除导致的不完整检测。
- 模拟 SDK 部分写入后的失败隔离及清理失败中止 2 项断言通过。1,024 条中单个坏样本不会退化为整批逐条重试;不向实际业务库或真实 SDK 注入坏特征。
- 最终提交的独立源码副本共 42 项 Flutter 测试通过(原有 15 项及本次新增 27 项)。
- Flutter 静态分析无 error。原有 66 条 warning/info 之外,新增两个 Floor
IN查询产生 4 条生成代码风格提示,业务源代码没有新增提示。
最终 Release 实际 App 原生装库测得 4,315 ms,5,537 条全部成功、失败 0。启动优化阶段首次快照逐行核对已存人脸 id、sampleId、userId 和 faceModel,全部不变;数据库 v15 完整性检查正常,首次快照的订单及挂单数量不变。后续同步测试期间的完整数据核对见 face-sync-reliability.md。Release 不输出 Dart info 级控制台日志,原生 FaceStartupPerf 仍可从 adb 读取。
最新原生回归装库 3,584 ms;另有 2 项真实 SDK 元数据更新/整批拒绝断言通过,不重写特征或业务数据库。
以上验证覆盖装库、字节一致性和基于本地照片的 SDK 检索;真人摄像头活体识别与实际支付需独立验收。
复测
使用 Flutter 3.27.4、JDK 17。原生测试入口只存在于 androidTest APK,不在发布包中。
# 仓库根目录
fvm flutter test --no-pub
fvm flutter analyze --no-pub --no-fatal-infos --no-fatal-warnings
# android 目录
./gradlew :app:testDebugUnitTest :app:assembleDebug :app:assembleDebugAndroidTest
adb install -r ../build/app/outputs/apk/debug/app-debug.apk
adb install -r ../build/app/outputs/apk/androidTest/debug/app-debug-androidTest.apk
# 仅独立 SQLite 和 SDK 失败隔离检查
adb shell am instrument -w -r \
com.example.pj_supermarket_cloud.test/io.flutter.plugins.facediscern.flutter.FaceStartupInstrumentation
# 真实数据 A/B:要求本机 SDK 已激活,所有样本已有完整特征和对应照片
adb shell am instrument -w -r -e benchmark true -e mode optimized -e batch 1000 -e rounds 3 \
com.example.pj_supermarket_cloud.test/io.flutter.plugins.facediscern.flutter.FaceStartupInstrumentation
# 旧算法对照,或调整 batch 为 100 / 500
adb shell am instrument -w -r -e benchmark true -e mode legacy -e rounds 3 \
com.example.pj_supermarket_cloud.test/io.flutter.plugins.facediscern.flutter.FaceStartupInstrumentation
测试不启动收银页面,不发起支付或远端人脸同步;照片只读,输出仅包含耗时、计数和通过状态。装库前需保留现有 APK 与数据库备份,使用 adb install -r 保留应用数据。