CusumptionMachine,并可作为其他 Android 项目的人脸识别接入参考/Users/liang/AndroidStudioProjects/CusumptionMachinefix_pay_by_face$architecting-android-face-recognition本次不是单纯把 FaceRecognitionPop 拆成几个类,也不是为了形式上的“分层”。真正目标是按照资源生命周期划清所有权:
业务页面
│ 订单、金额、菜品、支付、核验仍由页面负责
▼
FaceRecognitionLauncher
│ 统一入口、生命周期门禁、防重复展示
▼
FaceRecognitionPop
│ 只负责弹框 UI、交互和事件映射
▼
FaceRecognitionSession
│ 负责本次弹框的相机、识别、倒计时和资源释放
▼
FaceRecognitionRuntime
负责应用进程内的模型、人脸库和初始化状态
这套方式可以作为 Android 客户端多页面人脸识别的通用架构。可以跨项目复用的是职责边界、状态机、接口契约、迁移步骤和验收方法;不能不加判断地直接复制的是当前项目的厂商 SDK、摄像头参数、设备分支、页面类、支付接口和业务对象。
模型和人脸库通常是应用进程级重资源,初始化成功后应被多个页面共享。相机、帧检测、倒计时和本次识别结果只属于一次识别会话。弹框 UI 则只属于一次 View 生命周期。
如果三者全部放在一个弹框中,会产生以下矛盾:
因此,封装的第一原则不是“减少代码行数”,而是让资源的拥有者与资源的生命周期一致。
本项目虽然都使用人脸识别,但识别成功后的业务并不相同:
如果把这些业务也放进一个 FacePayManager,看似统一,实际会把识别能力与不同领域逻辑绑死。以后修改任意支付流程,都可能回归所有页面。
正确边界是:通用层只返回“识别到了谁、识别结束、用户取消、启动是否成功”等能力结果,具体支付、核验、订单清理和页面刷新仍由原业务页面决定。
原白屏问题的直接原因,是模型初始化失败后,识别弹框重新启动入口 Activity,并调用 Process.killProcess() 结束当前进程。这不是普通的识别失败,而是整个应用被重启。
菜单消费的菜品和自定义金额保存在当前页面内存中,进程被结束后,本单信息自然丢失,所以用户看到白屏、返回首页和订单被清空。
可复用的人脸识别层只允许结束本次识别会话,不允许:
应用级灾难恢复即使确有需要,也应该由应用最外层独立策略决定,不能藏在一个可复用弹框中。
模型失败时不再重启应用或杀进程。Session 停止本次识别、释放相机和异步任务,Pop 通过原有结束通道关闭,用户仍停留在原业务页面。
人脸识别层不修改 mSelectDishList,也不清空自定义金额对应的菜品。只要原 Activity 和应用进程没有被销毁:
本次没有新增“应用崩溃或系统杀进程后的订单草稿恢复”;解决的是人脸组件不再主动销毁应用进程。
FaceRecognitionRuntime 使用进程级状态统一协调模型和人脸库:
READY 时直接复用,不重复初始化。INITIALIZING 时多个调用方等待同一个任务。FaceRecognitionSession 只代表一次识别:
stop() 幂等释放相机、倒计时和异步任务。FaceRecognitionLauncher 收敛原来散落在页面内的查找、构造、Tag 和参数装配逻辑:
LaunchResult,让页面知道是否已有活动弹框。| 层 | 生命周期 | 负责内容 | 明确禁止 |
|---|---|---|---|
| Runtime | 应用进程级 | 授权、模型、人脸库、初始化状态、并发等待 | Activity、View、相机、支付、导航 |
| Session | 单次识别级 | 相机、帧检测、活体、倒计时、结果门闩、资源释放 | 页面订单、支付接口、应用重启 |
| Pop/UI | 视图级 | 预览、动画、按钮、用户信息、事件映射 | 重型初始化、底层相机、业务清单 |
| Launcher | 单次入口调用 | 生命周期门禁、防重复、Request、场景、展示结果 | 支付、核验、订单清理、长期持有宿主 |
| Business | 页面或领域级 | 菜品、金额、订单、支付、核验、结果刷新 | 了解 SDK 资源释放细节 |
| Adapter(按需) | SDK 或硬件级 | 隔离厂商模型、相机、帧格式和身份对象 | 绕过上述分层进入业务 |
封装前,弹框同时承担模型初始化、人脸库、相机、检测、倒计时、UI、结果、重试、资源释放和应用重启,既重又难以验证。
封装后,FaceRecognitionPop 仍然是用户看到的同一个弹框,但它只是:
重型模型初始化由 Runtime 共享,相机由 Session 严格管理。弹框从“资源管理器加业务控制器”恢复成普通 UI 组件。
FaceRecognitionRuntime。FaceRecognitionSession。FaceRecognitionPop 保留原公开接口和交互,内部改为委托 Session。LaunchActivity 接入同一个 Runtime 初始化链路。FaceRecognitionLauncher。FaceRecognitionPop 的位置从 8 个收敛为 0。已执行 :app:testDebugUnitTest :app:assembleDebug:
BUILD SUCCESSFUL。这些证据只能证明纯逻辑、编译和静态边界,不能代替真实摄像头、双屏和真实支付验收。
业务页面只构造一次请求,并保留自己的监听器:
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 组件调整接口,不能机械复制包名和方法签名。
以菜单消费为例,模型、相机或识别失败后的标准流程应为:
MainActivity 只处理提示和副屏收尾。这不是新增交互,而是把异常路径恢复成与原超时、取消路径一致的安全边界。
可以直接复用:
UNINITIALIZED → INITIALIZING → READY/FAILED 状态机。必须根据目标项目重新检查和实现:
User 与项目业务身份对象的转换。因此,更准确的说法是“形成一套通用接入方式和可迁移骨架”,不是“生成一个所有项目无需改动即可使用的二进制组件”。当多个项目确实使用同一 SDK、同一相机和相同结果契约时,再考虑抽成 Android Library;在此之前,先稳定架构边界更可靠。
按照 Runtime、Session、UI 委托、Launcher、逐页迁移的顺序推进。每一步保持外部接口兼容,避免一次同时修改所有业务页面和识别底层。
真机验证不能只测“正常识别成功”,应优先覆盖最容易破坏页面状态的路径:
| 优先级 | 场景 | 核心观察点 |
|---|---|---|
| P0 | 菜单消费模型失败 | 不白屏、不回首页、菜品和自定义金额保留、可再次支付 |
| P0 | 关闭后立即再次打开 | 相机能够重新获取,无黑屏和占用 |
| P0 | 快速连续点击 | 只出现一个弹框,只产生一条最终回调 |
| P1 | 单屏与双屏 | 预览方向、金额、副屏显示和结束收尾不变 |
| P1 | 7 个页面成功/取消/超时 | 原支付或核验入口、参数和结果处理不变 |
| P1 | 前后台和页面销毁 | 没有迟到回调、重复支付或崩溃 |
| P2 | 连续开关 20 次 | 相机、内存和识别状态保持稳定 |
故障注入必须使用 Debug 专用开关或可替换测试实现,不要通过破坏正式模型、删除生产数据或清除设备业务数据来测试。
技能名称:
$architecting-android-face-recognition
技能目录:
/Users/liang/.codex/skills/architecting-android-face-recognition/
技能包含:
SKILL.md:触发条件、实施流程和不可越过的边界。references/architecture-pattern.md:通用分层、契约、迁移和复用边界。references/acceptance-checklist.md:自动测试与真机验收清单。references/evals.md:正向、负向和邻接触发评测。agents/openai.yaml:技能展示名称、说明和默认调用提示。后续可直接这样调用:
使用 $architecting-android-face-recognition 分析并实现当前 Android 项目的人脸识别分层封装,保持现有业务和交互不变。
也可以只做审查:
使用 $architecting-android-face-recognition 审查当前人脸识别是否存在模型重复初始化、相机生命周期、重复弹框、迟到回调或失败重启问题,先输出证据和迁移建议。
技能的作用是让后续任务自动遵循同一套审查、设计、迁移和验收方法;它不会跳过对目标项目代码、SDK 和业务契约的实际检查。
代码和自动验证已经完成,但整体交付还需要真机确认:
只有这些真机项通过后,才能把“代码已落地”提升为“人脸识别改造整体验收完成”。
本项目后续新增人脸识别入口,应优先复用现有 FaceRecognitionLauncher → FaceRecognitionPop → FaceRecognitionSession → FaceRecognitionRuntime 调用链,业务页面只保留场景参数和业务回调。
其他 Android 项目后续接入人脸识别,应优先调用 $architecting-android-face-recognition 完成现状盘点、分层设计、渐进迁移和真机验收。架构和方法可以直接复用,具体 SDK、相机与业务适配必须基于目标项目重新实现和验证。