From 5479091959540894d44a486576145a32037e8a0d Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Thu, 2 Apr 2026 17:35:37 +0800 Subject: [PATCH] =?UTF-8?q?refactor(main):=20=E5=B0=86Thread=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E4=B8=BAlifecycleScope=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 替换CollectFragment中的Thread为activity.lifecycleScope.launch - 在MainActivity中添加FaceServer相关依赖和协程调度器 - 修改环境切换对话框以支持人脸数据重置功能 - 将照片拍摄和食物向量保存操作从Thread迁移到lifecycleScope - 优化人脸识别数据更新逻辑,移除不必要的线程创建 - 修改onTakePhotoSuccess方法为suspend函数并调整UI更新方式 - 更新主线程操作使用withContext(Dispatchers.Main)替代runOnUiThread - 在MainScreenPresentation中添加相机暂停功能 - 修复SensorScaleUtils中标定调用问题 - 扩展UserViewModel的人脸数据获取接口,支持回调处理 --- .../sw/dualscreen/activity/MainActivity.kt | 98 +++++++++++-------- .../activity/fragment/CollectFragment.kt | 4 +- .../presentation/MainScreenPresentation.kt | 2 + .../com/sw/dualscreen/sdk/SensorScaleUtils.kt | 3 +- .../sw/dualscreen/viewmodel/UserViewModel.kt | 82 +++++++++------- 5 files changed, 111 insertions(+), 78 deletions(-) diff --git a/app/src/main/java/com/sw/dualscreen/activity/MainActivity.kt b/app/src/main/java/com/sw/dualscreen/activity/MainActivity.kt index ab761db..f81d03c 100644 --- a/app/src/main/java/com/sw/dualscreen/activity/MainActivity.kt +++ b/app/src/main/java/com/sw/dualscreen/activity/MainActivity.kt @@ -68,10 +68,14 @@ import com.sw.plate.utils.LightManager import com.sw.plate.utils.ToastUtils import com.sw.plate.utils.arcface.facedb.FaceDatabase import com.sw.plate.utils.arcface.facedb.entity.FaceEntity +import com.sw.plate.utils.arcface.faceserver.FaceServer import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody import org.greenrobot.eventbus.Subscribe @@ -205,7 +209,16 @@ class MainActivity : BaseActivity() { } updateDateTime() binding.tvTitleTime.setOnClickListener { - EnvSwitchDialog(this).show() + EnvSwitchDialog(context = this, onEnvChanged = { + showWaitingDialog("重置人脸数据中……") + FaceServer.getInstance().clearAllFaces() + viewModel.getUserFaceCache(pageNo = 1, onSuccess = { + hideWaitingDialog() + ToastUtils.showToast("人脸数据已更新") + }, onFailure = { errMsg -> + ToastUtils.showToast(errMsg) + }) + }).show() } binding.ivSetting.setOnClickListener { isRefreshPage = false @@ -410,12 +423,19 @@ class MainActivity : BaseActivity() { LightManager.closeRedLight() LightManager.openGreenLight() showWaitingDialog("识别中,请稍后……") + lifecycleScope.launch { + delay(5000) + withContext(Dispatchers.Main) { + hideWaitingDialog() + } + } takePhoto(successCallback = { photoUri -> - Thread { + lifecycleScope.launch { onTakePhotoSuccess(photoUri) - }.start() + } failCount = 0 }, failureCallback = { + log("registerDataChange main,拍照异常,请重试") //ToastUtils.showToast("拍照异常,请重试") failCount++ hideWaitingDialog() @@ -809,9 +829,9 @@ class MainActivity : BaseActivity() { if (checkedItem?.isFromSearch == true) { //当前菜名为手动搜索选择,非识别结果,保存向量数据 - Thread { + lifecycleScope.launch { saveFoodVector(checkedItem!!) - }.start() + } } }, 300) } @@ -1074,44 +1094,44 @@ class MainActivity : BaseActivity() { if (list.isEmpty()) { return@runOnUiThread } - updateFaceData(list) + lifecycleScope.launch { + updateFaceData(list) + } } }) } } private fun updateFaceData(list: List) { - Thread { - val faceList = mutableListOf() - try { - list.forEach { model -> - if (model.faceDeleted == true) { - //删除数据 - FaceDatabase.getInstance(this).faceDao().deleteFaceById(model.userId) - } else { - //保存数据 - val faceEntity = FaceEntity( - model.userId, null, Base64.decode(model.faceFeatureStr) - ).also { - //1-会员、2-临时用户 - it.userType = if (model.member) "1" else "2" - it.cardNo = model.cardNo - } - faceList.add(faceEntity) + val faceList = mutableListOf() + try { + list.forEach { model -> + if (model.faceDeleted == true) { + //删除数据 + FaceDatabase.getInstance(this).faceDao().deleteFaceById(model.userId) + } else { + //保存数据 + val faceEntity = FaceEntity( + model.userId, null, Base64.decode(model.faceFeatureStr) + ).also { + //1-会员、2-临时用户 + it.userType = if (model.member) "1" else "2" + it.cardNo = model.cardNo } + faceList.add(faceEntity) } - } catch (e: Exception) { - e.printStackTrace() } - try { - if (faceList.isNotEmpty()) { - FaceDatabase.getInstance(this).faceDao().insert(faceList) - } - recognizeViewModel.refreshFaceList(); - } catch (e: Exception) { - e.printStackTrace() + } catch (e: Exception) { + e.printStackTrace() + } + try { + if (faceList.isNotEmpty()) { + FaceDatabase.getInstance(this).faceDao().insert(faceList) } - }.start() + recognizeViewModel.refreshFaceList(); + } catch (e: Exception) { + e.printStackTrace() + } } private fun sendFaceData(faceData: String) { @@ -1125,16 +1145,14 @@ class MainActivity : BaseActivity() { private var tcpClient: TcpClient? = null - private fun onTakePhotoSuccess(photoUri: Uri) { + private suspend fun onTakePhotoSuccess(photoUri: Uri) { lastPhotoUri = photoUri log("registerDataChange main,takePhoto耗时:${System.currentTimeMillis() - startTime}") startTime = System.currentTimeMillis() log("registerDataChange photoUri = ${photoUri.path}") // viewModel.getIdentifiedFoodList() - lifecycleScope.launch { - ImageUtil.uriToBitmap(this@MainActivity, photoUri)?.let { bitmap -> - queryFoodData(bitmap) - } + ImageUtil.uriToBitmap(this@MainActivity, photoUri)?.let { bitmap -> + queryFoodData(bitmap) } } @@ -1163,7 +1181,7 @@ class MainActivity : BaseActivity() { hideWaitingDialog() log("registerDataChange photoUri 识别数据名称:$foodName") - runOnUiThread { + withContext(Dispatchers.Main) { binding.flPay.run { //0-计费,1-不计费 val chargeMode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0) @@ -1176,7 +1194,7 @@ class MainActivity : BaseActivity() { LightManager.closeRedLight() //binding.layoutRescan.visibility = View.VISIBLE - runOnUiThread { + withContext(Dispatchers.Main) { binding.tvToSearch.let { it.text = "未识别到,手动搜索" it.visible() diff --git a/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt b/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt index b148eab..0e84beb 100644 --- a/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt +++ b/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt @@ -118,11 +118,11 @@ class CollectFragment : BaseFragment() { collectionAdapter.notifyItemChanged(index) } // settingActivity?.hideWaitingDialog() - Thread { + activity?.lifecycleScope?.launch { ImageUtil.uriToBitmap(requireActivity(), uri)?.let { bitmap -> getImageVector(index, bitmap) } - }.start() + } } catch (e: Exception) { e.printStackTrace() settingActivity?.hideWaitingDialog() diff --git a/app/src/main/java/com/sw/dualscreen/presentation/MainScreenPresentation.kt b/app/src/main/java/com/sw/dualscreen/presentation/MainScreenPresentation.kt index ed8135c..3c6fefd 100644 --- a/app/src/main/java/com/sw/dualscreen/presentation/MainScreenPresentation.kt +++ b/app/src/main/java/com/sw/dualscreen/presentation/MainScreenPresentation.kt @@ -1041,6 +1041,7 @@ class MainScreenPresentation( return@runOnUiThread } } + pauseCamera() step3ShowRecognizeResult(userId) }) @@ -1313,6 +1314,7 @@ class MainScreenPresentation( mealPickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0 Timber.tag(TAG).i("$lastFaceTrackId 用户离开") lastFaceTrackId = -1 + //resumeCamera() Timber.tag(TAG) .d("postUserData userNutritionData是否null: ${userNutrition == null}, currentFood是否null: ${currentFood == null},currentUserId是否null:${currentUserId == null}") if (userNutrition == null || currentFood == null || currentUserId == null) { diff --git a/app/src/main/java/com/sw/dualscreen/sdk/SensorScaleUtils.kt b/app/src/main/java/com/sw/dualscreen/sdk/SensorScaleUtils.kt index cfc52b5..7d2d65a 100644 --- a/app/src/main/java/com/sw/dualscreen/sdk/SensorScaleUtils.kt +++ b/app/src/main/java/com/sw/dualscreen/sdk/SensorScaleUtils.kt @@ -87,8 +87,7 @@ object SensorScaleUtils { // Thread.sleep(1000) // TODO: 暂时不标定--------- - //zero() - + zero() }.start() } } diff --git a/app/src/main/java/com/sw/dualscreen/viewmodel/UserViewModel.kt b/app/src/main/java/com/sw/dualscreen/viewmodel/UserViewModel.kt index a7c82a4..938bce0 100644 --- a/app/src/main/java/com/sw/dualscreen/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/sw/dualscreen/viewmodel/UserViewModel.kt @@ -91,54 +91,68 @@ class UserViewModel : BaseViewModel() { /** * 获取人脸数据 */ - fun getUserFaceCache(pageNo: Int = 1, pageSize: Int = PAGE_SIZE) { + fun getUserFaceCache( + pageNo: Int = 1, + pageSize: Int = PAGE_SIZE, + onSuccess: () -> Unit = {}, + onFailure: (String) -> Unit = {} + ) { + // Initialize current page number var currentPageNo = pageNo + // Debug log to track current page Timber.tag(TAG).d("getUserFaceCache index = $currentPageNo") + // Launch a coroutine to perform the face data retrieval launch { + // Set loading state to false _loadFaceResult.value = false //获取全量数据时,时间戳改为0 SpTool.lastFaceTimestamp = 0 val response = repository.getUserFaceCache(currentPageNo) - if (parseResponse(response)) { - // 获取成功一次后缓存状态 - SPUtil.getInstance().put(GlobalKey.KEY_FIRST_RUN, true) - withContext(Dispatchers.Default) { - val list: List = response.data ?: emptyList() - if (pageNo == 1 && list.isEmpty()) { - //ToastUtils.showToast("查询人脸数据为空") - return@withContext - } - //val item = list.map { it.member } - //Timber.tag(TAG).d("getUserFaceCache item = $item") - val faceEntity = list.map { - FaceEntity(it.userId, null, Base64.decode(it.faceFeatureStr)).also { item -> - item.userType = if (it.member) "1" else "2" - item.cardNo = it.cardNo - } - } - faceApi.updateFaceData(currentPageNo, faceEntity) - if (list.size >= pageSize) { - lastFaceTimestamp = list.last().faceUpdateTimestamp ?: 0L - currentPageNo++ - getUserFaceCache(currentPageNo) - return@withContext - } - //查询完成所有数据,保存时间戳 - if (list.isNotEmpty()) { - //非空最后一条数据的是时间戳,空使用上次list最后一条时间戳 - lastFaceTimestamp = list.last().faceUpdateTimestamp ?: 0L - } - SpTool.lastFaceTimestamp = lastFaceTimestamp - _loadFaceResult.value = true + val check = parseResponse(response) + if (!check) { + // 获取失败 + onFailure("获取人脸数据失败,${response.msg}(${response.code})") + return@launch + } + // 获取成功一次后缓存状态 + SPUtil.getInstance().put(GlobalKey.KEY_FIRST_RUN, true) + withContext(Dispatchers.Default) { + val list: List = response.data ?: emptyList() + if (pageNo == 1 && list.isEmpty()) { + //ToastUtils.showToast("查询人脸数据为空") + onFailure("查询人脸数据为空") + return@withContext } - + //val item = list.map { it.member } + //Timber.tag(TAG).d("getUserFaceCache item = $item") + val faceEntity = list.map { + FaceEntity(it.userId, null, Base64.decode(it.faceFeatureStr)).also { item -> + item.userType = if (it.member) "1" else "2" + item.cardNo = it.cardNo + } + } + faceApi.updateFaceData(currentPageNo, faceEntity) + if (list.size >= pageSize) { + lastFaceTimestamp = list.last().faceUpdateTimestamp ?: 0L + currentPageNo++ + getUserFaceCache(currentPageNo) + return@withContext + } + //查询完成所有数据,保存时间戳 + if (list.isNotEmpty()) { + //非空最后一条数据的是时间戳,空使用上次list最后一条时间戳 + lastFaceTimestamp = list.last().faceUpdateTimestamp ?: 0L + } + SpTool.lastFaceTimestamp = lastFaceTimestamp + _loadFaceResult.value = true + onSuccess() + } // val nextPageIndex = response.result?.nextPageIndex ?: -1 // if (nextPageIndex > 0) { // getUserFaceCache(nextPageIndex) // } else { // _loadFaceResult.value = true // } - } } }