diff --git a/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt b/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt index 1946dbe..fb2bbd7 100644 --- a/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt +++ b/app/src/main/java/com/sw/platecabinet/activity/LoginByFaceActivity.kt @@ -54,7 +54,6 @@ import com.sw.platecabinet.utils.BitmapSaver import com.sw.platecabinet.utils.CountDownUtil import com.sw.platecabinet.utils.Debouncer import com.sw.platecabinet.utils.LogFileUtil -import com.sw.platecabinet.utils.PlateUtils import com.sw.platecabinet.utils.RegisterCallbackHandler import com.sw.platecabinet.utils.SoundPoolUtil import com.sw.platecabinet.utils.mego.PlcCallback @@ -295,7 +294,7 @@ class LoginByFaceActivity : BaseActivity(), } recognizeViewModel.recognizeUserId.observe(this) { result: CompareResult? -> - loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}") + loadLogInfo("collectFace,recognizeUserId observe userId=${result?.faceEntity?.userName},similar=${result?.similar},userType=${result?.faceEntity?.userType}") loadFaceRecognizeResult3(result) } @@ -319,7 +318,7 @@ class LoginByFaceActivity : BaseActivity(), */ private val faceSuccessThreshold by lazy { ConfigUtil.getRecognizeThreshold(this) } - private val openPlateDebouncer = Debouncer(5000) + private val openPlateDebouncer = Debouncer(3000) private val isCollecting = AtomicBoolean(false) private fun loadFaceRecognizeResult3(result: CompareResult?) { @@ -371,7 +370,7 @@ class LoginByFaceActivity : BaseActivity(), currentUserId = userId // 防抖,防止重复开闸 - openPlateDebouncer.debounce { + openPlateDebouncer.debounce("openPlateDebouncer") { openPlate(userId) } } @@ -400,6 +399,8 @@ class LoginByFaceActivity : BaseActivity(), text = "新用户采集" } binding.ivCollectPhoto.gone() + openPlateDebouncer.reset() + plateEnable = true resumeCamera() } @@ -409,46 +410,47 @@ class LoginByFaceActivity : BaseActivity(), * 打开餐盘柜:先获取 PLC 状态,有盘则出盘,无盘则播放提示音 */ private fun openPlate(userId: String?) { + loadLogInfo("collectFace,openPlate: userId=$userId,plateEnable=$plateEnable") if (plateEnable.not()) return - loadLogInfo("collectFace,openPlate: userId=$userId") - showWaitingDialog("检测餐盘中,请稍候……") +// showWaitingDialog("检测餐盘中,请稍候……") PlcHelper.getInstance(this).getStatus(object : PlcCallback { - override fun onSuccess(result: PlcStatus) { - hideWaitingDialog() - if (result.isEmptyPlate) { - // 无餐盘,播放提示音并返回首页 - loadLogInfo("collectFace,openPlate: 暂无餐盘,isEmptyPlate=true,isCollectFace=$isCollectFace") + override fun onSuccess(result: PlcStatus) { + hideWaitingDialog() + if (result.isEmptyPlate) { + // 无餐盘,播放提示音并返回首页 + loadLogInfo("collectFace,openPlate: 暂无餐盘,isEmptyPlate=true,isCollectFace=$isCollectFace") + SoundPoolUtil.getInstance().play(NO_PLATE_REMIND, 0) + goInitActivity() + return + } + // 有餐盘,播放识别成功音效并调用出盘 + loadLogInfo("collectFace,openPlate: 检测到餐盘,开始出盘") + SoundPoolUtil.getInstance().play(RECOGNIZE_SUCCESS, 0) + plateEnable = false + recognizeCountDownUtil.cancelCountDown() + PlcHelper.getInstance(this@LoginByFaceActivity) + .outPlate(object : PlcCallback { + override fun onSuccess(result: String) { + loadLogInfo("collectFace,openPlate: 出盘成功,result=$result") + goInitActivity() + } + + override fun onError(message: String) { + ToastUtils.showToast("出盘失败,message=$message") + loadLogInfo("collectFace,openPlate: 出盘失败,message=$message") + goInitActivity() + } + }) + } + + override fun onError(message: String) { + hideWaitingDialog() + // 状态获取失败,保底播放无盘提示 + loadLogInfo("collectFace,openPlate: 获取PLC状态失败,message=$message") SoundPoolUtil.getInstance().play(NO_PLATE_REMIND, 0) goInitActivity() - return } - // 有餐盘,播放识别成功音效并调用出盘 - loadLogInfo("collectFace,openPlate: 检测到餐盘,开始出盘") - SoundPoolUtil.getInstance().play(RECOGNIZE_SUCCESS, 0) - plateEnable = false - recognizeCountDownUtil.cancelCountDown() - PlcHelper.getInstance(this@LoginByFaceActivity) - .outPlate(object : PlcCallback { - override fun onSuccess(result: String) { - loadLogInfo("collectFace,openPlate: 出盘成功,result=$result") - goInitActivity() - } - - override fun onError(message: String) { - loadLogInfo("collectFace,openPlate: 出盘失败,message=$message") - goInitActivity() - } - }) - } - - override fun onError(message: String) { - hideWaitingDialog() - // 状态获取失败,保底播放无盘提示 - loadLogInfo("collectFace,openPlate: 获取PLC状态失败,message=$message") - SoundPoolUtil.getInstance().play(NO_PLATE_REMIND, 0) - goInitActivity() - } - }) + }) } private val openDebouncer by lazy { Debouncer(5000) } @@ -598,9 +600,10 @@ class LoginByFaceActivity : BaseActivity(), binding.btnCollect.tag = "" binding.tvSimilarValue.text = "" retryFailCount = 0 - + // 人脸离开时,发送空帧清除 ViewModel 内部识别缓存,防止再次进入时使用旧数据 + recognizeViewModel.onPreviewFrame(emptyFrame, true) if (binding.llCollectButton.isVisible) { - leftDebouncer.debounce { leftCountDown() } + leftDebouncer.debounce("leftDebouncer") { leftCountDown() } } } recognizeViewModel.clearLeftFace(facePreviewInfoList) @@ -724,13 +727,13 @@ class LoginByFaceActivity : BaseActivity(), // private var tempUserId: String = "" override fun onResume() { super.onResume() + plateEnable = true try { if (isFirstOpen) { isFirstOpen = false goInitActivity() return } - lifecycleScope.launch { try { val faceDao = FaceDatabase.getInstance(this@LoginByFaceActivity).faceDao() @@ -764,6 +767,7 @@ class LoginByFaceActivity : BaseActivity(), plateEnable = true collectCount = 0 // tempUserId = "${System.currentTimeMillis()}" + openPlateDebouncer.reset() resumeCamera() registerHandler = RegisterCallbackHandler() @@ -852,7 +856,7 @@ class LoginByFaceActivity : BaseActivity(), ToastUtils.showToast("获取人脸照片失败") return } - uploadFaceDebouncer.debounce { + uploadFaceDebouncer.debounce("uploadFaceDebouncer") { addUserFace(faceData, imageFile) } } diff --git a/app/src/main/java/com/sw/platecabinet/utils/LogFileUtil.java b/app/src/main/java/com/sw/platecabinet/utils/LogFileUtil.java index 9493c6d..1288775 100644 --- a/app/src/main/java/com/sw/platecabinet/utils/LogFileUtil.java +++ b/app/src/main/java/com/sw/platecabinet/utils/LogFileUtil.java @@ -19,10 +19,15 @@ import java.io.StringReader; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public class LogFileUtil { private static boolean isSaveLog = true; - public static final String FILE_STR_PATH = App.getContext().getExternalCacheDir().getAbsolutePath(); + // 使用内部存储,不会被系统缓存清理策略影响 + public static final String FILE_STR_PATH = new File(App.getContext().getFilesDir(), "logs").getAbsolutePath(); + // 单线程执行器,保证日志串行写入,避免多线程并发竞争 + private static final ExecutorService LOG_EXECUTOR = Executors.newSingleThreadExecutor(); public static final int SIZETYPE_B = 1;//获取文件大小单位为B的double值 public static final int SIZETYPE_KB = 2;//获取文件大小单位为KB的double值 @@ -610,24 +615,20 @@ public class LogFileUtil { if (!isSaveLog) { return; } - new Thread() { - @Override - public void run() { - super.run(); - StringBuilder sb = new StringBuilder(); - for (String log : logs) { - sb.append("===") - .append((AppUtil.formatDateGetCurrentTime())) - .append("===") - .append("\n") - .append(log) - .append("\n"); - } - String fileName = "log" + AppUtil.formatDateGetDay(System.currentTimeMillis()) + ".txt"; - LogFileUtil.saveStrFile(sb.toString(), fileName, FILE_STR_PATH, true); - + // 提交到单线程队列,串行执行,避免并发写入竞争 + LOG_EXECUTOR.execute(() -> { + StringBuilder sb = new StringBuilder(); + for (String log : logs) { + sb.append("===") + .append((AppUtil.formatDateGetCurrentTime())) + .append("===") + .append("\n") + .append(log) + .append("\n"); } - }.start(); + String fileName = "log" + AppUtil.formatDateGetDay(System.currentTimeMillis()) + ".txt"; + LogFileUtil.saveStrFile(sb.toString(), fileName, FILE_STR_PATH, true); + }); } private static final String[][] MIME_MapTable = {