变更日期:2026-09-01
项目仓库:/Users/liang/AndroidStudioProjects/CusumptionMachine
当前分支:fix_pay_by_face
代码基线:804e617
变更状态:自动验证完成,真机待验
FaceRecognitionPop 同时承担模型初始化、数据库装载、相机识别、倒计时、UI 和异常恢复。模型初始化失败时,弹框会重新启动应用入口并结束当前进程,导致白屏、返回首页,以及菜单消费内存订单丢失。
本次在不改变现有业务和现有交互的前提下,将模型初始化和单次相机会话从弹框拆出,并删除业务弹框内的应用重启及杀进程行为。
保留 7 个业务页面现有调用方式。
保留 FaceRecognitionPop 构造方法和 FaceRecognitionResultListener。
保留识别后展示用户、“重试”“确认”和“取消”交互。
保留用户确认后才进入支付或核验的规则。
保留超时和取消后自动关闭弹框的规则。
不修改支付接口、订单核验接口、结果弹框和订单清空逻辑。
不修改布局、资源文案和服务端协议。
新增:
faceRecognition/runtime/FaceRecognitionRuntime.java
faceRecognition/runtime/FaceRecognitionRuntimeState.java
实现内容:
使用进程级单例统一模型初始化状态。
使用 UNINITIALIZED、INITIALIZING、READY、FAILED 状态。
初始化期间的多个调用者共享同一次初始化。
初始化失败后允许下一次主动请求受控重试。
使用初始化代次过滤旧任务迟到回调。
捕获初始化器同步异常并转换为失败回调。
只保存 ApplicationContext 语义,不保存 Activity、Fragment 或 View。
每个调用方获得可取消的 InitRequest;取消只解除当前页面回调,不中断共享初始化。
记录本地人脸数据是否已由启动流程装载,避免弹框重复装载。
修改:
LaunchActivity.java
实现内容:
原 initCustomFaceModel() 改为调用 FaceRecognitionRuntime.initialize()。
原许可证成功状态、许可证异常处理、数据库初始化和导航流程不变。
本地人脸数据库装载后调用 markFaceDataReady()。
onDestroy() 取消启动页自身的初始化请求,避免 Runtime 保留已销毁页面回调。
启动页自身原有重试和退出策略未修改。
新增:
faceRecognition/session/FaceRecognitionSession.java
faceRecognition/session/FaceRecognitionSessionCallback.java
faceRecognition/session/FaceRecognitionSessionGate.java
实现内容:
Runtime 就绪后才启动相机和识别倒计时。
保持现有设备类型对应的摄像头方向。
保持主屏和可选副屏预览参数。
接收相机帧并继续调用现有 FaceSDKManager.onDetectCheck()。
匹配成功后停止相机与倒计时并返回原 SDK User。
保持当前弹框内“重试”操作。
使用会话代次和结果门闩过滤重复结果、旧会话结果和关闭后的迟到回调。
stop() 取消当前 Runtime 等待请求,但不影响其他页面共享的模型初始化。
stop() 幂等释放倒计时、异步任务和相机预览。
修改:
view/FaceRecognitionPop.java
实现内容:
保留原构造方法、确认文字设置方法和三个业务结果回调。
保留原布局、用户信息、金额、重试、确认和取消交互。
内部创建 FaceRecognitionSession 并映射会话事件。
模型异常继续通过原识别结束通道关闭弹框和通知页面收尾。
删除弹框内模型初始化、数据库重复装载、直接相机检测代码。
删除弹框内 restartSelf()、应用入口启动和 Process.killProcess()。
onDestroyView() 停止并释放 Session。
新增:
faceRecognition/runtime/FaceRecognitionRuntimeTest.java
faceRecognition/session/FaceRecognitionSessionGateTest.java
覆盖内容:
并发请求只执行一次模型初始化。
初始化期间 SDK 标记提前更新时,所有等待者仍由当前初始化回调统一收口。
SDK 已就绪时不重复初始化。
初始化失败后受控重试。
旧初始化回调不能覆盖新任务状态。
人脸数据兜底装载只执行一次。
初始化器同步异常转换为失败状态。
已取消的初始化请求不再分发或持有页面回调,共享初始化状态仍正常完成。
同一会话只接收一次匹配结果。
会话关闭后拒绝迟到回调。
新会话使旧会话回调失效。
测试采用先失败、再实现、再转为通过的红绿流程。
执行命令:
JAVA_HOME=/Users/liang/Library/Java/JavaVirtualMachines/corretto-1.8.0_482/Contents/Home \ sh ./gradlew :app:testDebugUnitTest :app:assembleDebug
结果:
BUILD SUCCESSFUL。
Debug 单元测试:41 个。
失败:0。
错误:0。
跳过:0。
Debug APK 构建成功。
静态业务检查:
仍有 7 个业务页面调用 FaceRecognitionPop。
仍有 8 个 FaceRecognitionPop 构造位置。
7 个业务页面没有源码变更。
FaceRecognitionPop 原构造方法和三个回调仍存在。
人脸封装代码没有调用 mSelectDishList.clear()。
FaceRecognitionPop 不再包含模型初始化、数据库装载、相机底层调用、应用重启或杀进程代码。
正常业务流程保持不变:
识别到用户后仍显示当前结果页面。
用户点击“重试”仍在当前弹框重新识别。
用户点击“确认”后才调用页面原业务。
超时和取消仍自动关闭弹框。
各页面仍调用原支付或核验接口。
修复后的异常流程:
模型不可用时停止 Session。
通过原结束回调完成主屏和副屏收尾。
自动关闭弹框并停留在原业务页面。
不重新启动应用,不结束进程。
菜单消费订单列表和自定义金额不会被人脸封装清空,用户可以重新点击支付。
以下硬件行为无法由本地 JVM 和 APK 构建证明,仍需设备验收:
菜单消费单屏摄像头预览、识别、重试、确认、超时和取消。
菜单消费双屏主副屏预览及结束收尾。
模型故障注入后不白屏、不返回首页、订单保留。
已选菜品和自定义金额在失败后的页面实际显示。
再次点击支付后摄像头可以重新打开并识别。
其余 6 个页面逐一完成成功、超时和取消回归。
连续打开关闭弹框后的相机资源稳定性。
未执行 Git 暂存。
未执行 Git 提交。
未执行 Git 推送。
未创建或切换分支。