Android 人脸识别通用封装与项目复用总结

1. 核心结论

本次不是单纯把 FaceRecognitionPop 拆成几个类,也不是为了形式上的“分层”。真正目标是按照资源生命周期划清所有权:

业务页面
   │  订单、金额、菜品、支付、核验仍由页面负责
   ▼
FaceRecognitionLauncher
   │  统一入口、生命周期门禁、防重复展示
   ▼
FaceRecognitionPop
   │  只负责弹框 UI、交互和事件映射
   ▼
FaceRecognitionSession
   │  负责本次弹框的相机、识别、倒计时和资源释放
   ▼
FaceRecognitionRuntime
      负责应用进程内的模型、人脸库和初始化状态

这套方式可以作为 Android 客户端多页面人脸识别的通用架构。可以跨项目复用的是职责边界、状态机、接口契约、迁移步骤和验收方法;不能不加判断地直接复制的是当前项目的厂商 SDK、摄像头参数、设备分支、页面类、支付接口和业务对象。

2. 为什么必须这样封装

2.1 不同资源的生命周期不同

模型和人脸库通常是应用进程级重资源,初始化成功后应被多个页面共享。相机、帧检测、倒计时和本次识别结果只属于一次识别会话。弹框 UI 则只属于一次 View 生命周期。

如果三者全部放在一个弹框中,会产生以下矛盾:

因此,封装的第一原则不是“减少代码行数”,而是让资源的拥有者与资源的生命周期一致。

2.2 人脸识别能力不等于支付业务

本项目虽然都使用人脸识别,但识别成功后的业务并不相同:

如果把这些业务也放进一个 FacePayManager,看似统一,实际会把识别能力与不同领域逻辑绑死。以后修改任意支付流程,都可能回归所有页面。

正确边界是:通用层只返回“识别到了谁、识别结束、用户取消、启动是否成功”等能力结果,具体支付、核验、订单清理和页面刷新仍由原业务页面决定。

2.3 可复用组件不能决定应用生死

原白屏问题的直接原因,是模型初始化失败后,识别弹框重新启动入口 Activity,并调用 Process.killProcess() 结束当前进程。这不是普通的识别失败,而是整个应用被重启。

菜单消费的菜品和自定义金额保存在当前页面内存中,进程被结束后,本单信息自然丢失,所以用户看到白屏、返回首页和订单被清空。

可复用的人脸识别层只允许结束本次识别会话,不允许:

应用级灾难恢复即使确有需要,也应该由应用最外层独立策略决定,不能藏在一个可复用弹框中。

3. 本次封装解决了什么问题

3.1 修复模型失败后的白屏和返回首页

模型失败时不再重启应用或杀进程。Session 停止本次识别、释放相机和异步任务,Pop 通过原有结束通道关闭,用户仍停留在原业务页面。

3.2 保留菜单消费本单信息

人脸识别层不修改 mSelectDishList,也不清空自定义金额对应的菜品。只要原 Activity 和应用进程没有被销毁:

本次没有新增“应用崩溃或系统杀进程后的订单草稿恢复”;解决的是人脸组件不再主动销毁应用进程。

3.3 避免重复模型初始化和就绪竞态

FaceRecognitionRuntime 使用进程级状态统一协调模型和人脸库:

3.4 避免相机和回调越过弹框生命周期

FaceRecognitionSession 只代表一次识别:

3.5 统一多页面入口并防止重复弹框

FaceRecognitionLauncher 收敛原来散落在页面内的查找、构造、Tag 和参数装配逻辑:

4. 每一层的职责和边界

生命周期 负责内容 明确禁止
Runtime 应用进程级 授权、模型、人脸库、初始化状态、并发等待 Activity、View、相机、支付、导航
Session 单次识别级 相机、帧检测、活体、倒计时、结果门闩、资源释放 页面订单、支付接口、应用重启
Pop/UI 视图级 预览、动画、按钮、用户信息、事件映射 重型初始化、底层相机、业务清单
Launcher 单次入口调用 生命周期门禁、防重复、Request、场景、展示结果 支付、核验、订单清理、长期持有宿主
Business 页面或领域级 菜品、金额、订单、支付、核验、结果刷新 了解 SDK 资源释放细节
Adapter(按需) SDK 或硬件级 隔离厂商模型、相机、帧格式和身份对象 绕过上述分层进入业务

5. 为什么对弹框来说不再“太重”

封装前,弹框同时承担模型初始化、人脸库、相机、检测、倒计时、UI、结果、重试、资源释放和应用重启,既重又难以验证。

封装后,FaceRecognitionPop 仍然是用户看到的同一个弹框,但它只是:

  1. 创建并绑定一次 Session。
  2. 根据 Session 事件更新预览、动画和用户信息。
  3. 把用户确认、取消、超时映射回原业务监听器。
  4. 在 View 销毁时停止 Session。

重型模型初始化由 Runtime 共享,相机由 Session 严格管理。弹框从“资源管理器加业务控制器”恢复成普通 UI 组件。

6. 使用时能获得什么好处

6.1 对业务开发

6.2 对稳定性

6.3 对测试和维护

7. 本项目已经落地的内容

7.1 一期:Runtime + Session + Pop

7.2 二期:Launcher + Request + Scene + Gate

7.3 当前自动验证证据

已执行 :app:testDebugUnitTest :app:assembleDebug

这些证据只能证明纯逻辑、编译和静态边界,不能代替真实摄像头、双屏和真实支付验收。

8. 标准使用方式

业务页面只构造一次请求,并保留自己的监听器:

FaceRecognitionRequest request = FaceRecognitionRequest.builder()
        .scene(FaceRecognitionScene.MENU_CONSUMPTION)
        .orderAmount(currentAmount)
        .secondaryPreview(optionalSecondaryPreview)
        .build();

FaceRecognitionLauncher.LaunchResult result =
        FaceRecognitionLauncher.launch(this, request, recognitionResultListener);

if (!result.hasActiveDialog()) {
    restorePageEntryState();
}

这段示意只表达调用边界。不同项目应根据自身 Fragment 版本、页面基类、结果对象和 UI 组件调整接口,不能机械复制包名和方法签名。

9. 标准失败和再次支付行为

以菜单消费为例,模型、相机或识别失败后的标准流程应为:

  1. 本次 Session 接收唯一失败结果。
  2. 停止相机、倒计时和检测任务。
  3. Pop 按当前项目原有交互自动关闭。
  4. MainActivity 只处理提示和副屏收尾。
  5. 菜品列表、自定义金额和本单状态保持不变。
  6. 用户再次点击支付,Launcher 创建一个全新 Session。

这不是新增交互,而是把异常路径恢复成与原超时、取消路径一致的安全边界。

10. 跨项目时哪些可以直接复用

可以直接复用:

11. 跨项目时哪些必须重新适配

必须根据目标项目重新检查和实现:

因此,更准确的说法是“形成一套通用接入方式和可迁移骨架”,不是“生成一个所有项目无需改动即可使用的二进制组件”。当多个项目确实使用同一 SDK、同一相机和相同结果契约时,再考虑抽成 Android Library;在此之前,先稳定架构边界更可靠。

12. 新项目的标准落地流程

步骤 1:盘点现状

步骤 2:固化不变量

步骤 3:先写可验证边界

步骤 4:渐进迁移

按照 Runtime、Session、UI 委托、Launcher、逐页迁移的顺序推进。每一步保持外部接口兼容,避免一次同时修改所有业务页面和识别底层。

步骤 5:分层验收

13. 真机验收的关键策略

真机验证不能只测“正常识别成功”,应优先覆盖最容易破坏页面状态的路径:

优先级 场景 核心观察点
P0 菜单消费模型失败 不白屏、不回首页、菜品和自定义金额保留、可再次支付
P0 关闭后立即再次打开 相机能够重新获取,无黑屏和占用
P0 快速连续点击 只出现一个弹框,只产生一条最终回调
P1 单屏与双屏 预览方向、金额、副屏显示和结束收尾不变
P1 7 个页面成功/取消/超时 原支付或核验入口、参数和结果处理不变
P1 前后台和页面销毁 没有迟到回调、重复支付或崩溃
P2 连续开关 20 次 相机、内存和识别状态保持稳定

故障注入必须使用 Debug 专用开关或可替换测试实现,不要通过破坏正式模型、删除生产数据或清除设备业务数据来测试。

14. 已形成的可复用技能

技能名称:

$architecting-android-face-recognition

技能目录:

/Users/liang/.codex/skills/architecting-android-face-recognition/

技能包含:

后续可直接这样调用:

使用 $architecting-android-face-recognition 分析并实现当前 Android 项目的人脸识别分层封装,保持现有业务和交互不变。

也可以只做审查:

使用 $architecting-android-face-recognition 审查当前人脸识别是否存在模型重复初始化、相机生命周期、重复弹框、迟到回调或失败重启问题,先输出证据和迁移建议。

技能的作用是让后续任务自动遵循同一套审查、设计、迁移和验收方法;它不会跳过对目标项目代码、SDK 和业务契约的实际检查。

15. 当前项目还需要完成什么

代码和自动验证已经完成,但整体交付还需要真机确认:

只有这些真机项通过后,才能把“代码已落地”提升为“人脸识别改造整体验收完成”。

16. 最终决策

本项目后续新增人脸识别入口,应优先复用现有 FaceRecognitionLauncher → FaceRecognitionPop → FaceRecognitionSession → FaceRecognitionRuntime 调用链,业务页面只保留场景参数和业务回调。

其他 Android 项目后续接入人脸识别,应优先调用 $architecting-android-face-recognition 完成现状盘点、分层设计、渐进迁移和真机验收。架构和方法可以直接复用,具体 SDK、相机与业务适配必须基于目标项目重新实现和验证。