refactor(main): 将Thread替换为lifecycleScope并优化协程处理

- 替换CollectFragment中的Thread为activity.lifecycleScope.launch
- 在MainActivity中添加FaceServer相关依赖和协程调度器
- 修改环境切换对话框以支持人脸数据重置功能
- 将照片拍摄和食物向量保存操作从Thread迁移到lifecycleScope
- 优化人脸识别数据更新逻辑,移除不必要的线程创建
- 修改onTakePhotoSuccess方法为suspend函数并调整UI更新方式
- 更新主线程操作使用withContext(Dispatchers.Main)替代runOnUiThread
- 在MainScreenPresentation中添加相机暂停功能
- 修复SensorScaleUtils中标定调用问题
- 扩展UserViewModel的人脸数据获取接口,支持回调处理
This commit is contained in:
2026-04-02 17:35:37 +08:00
parent 12da153df3
commit 5479091959
5 changed files with 111 additions and 78 deletions
@@ -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<ActivityMainBinding>() {
}
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<ActivityMainBinding>() {
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<ActivityMainBinding>() {
if (checkedItem?.isFromSearch == true) {
//当前菜名为手动搜索选择,非识别结果,保存向量数据
Thread {
lifecycleScope.launch {
saveFoodVector(checkedItem!!)
}.start()
}
}
}, 300)
}
@@ -1074,44 +1094,44 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
if (list.isEmpty()) {
return@runOnUiThread
}
updateFaceData(list)
lifecycleScope.launch {
updateFaceData(list)
}
}
})
}
}
private fun updateFaceData(list: List<UserFaceModel>) {
Thread {
val faceList = mutableListOf<FaceEntity>()
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<FaceEntity>()
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<ActivityMainBinding>() {
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<ActivityMainBinding>() {
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<ActivityMainBinding>() {
LightManager.closeRedLight()
//binding.layoutRescan.visibility = View.VISIBLE
runOnUiThread {
withContext(Dispatchers.Main) {
binding.tvToSearch.let {
it.text = "未识别到,手动搜索"
it.visible()
@@ -118,11 +118,11 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
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()
@@ -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) {
@@ -87,8 +87,7 @@ object SensorScaleUtils {
// Thread.sleep(1000)
// TODO: 暂时不标定---------
//zero()
zero()
}.start()
}
}
@@ -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<UserFaceModel> = 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<UserFaceModel> = 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
// }
}
}
}