refactor(utils): 重构 FoodVectorTool 并优化 zero() 提示控制

- 重构 FoodVectorTool:移除单例 pageNum 状态污染问题,分页逻辑下沉为私有 fetchPage,saveFoodVector 改为私有,PAGE_SIZE 移入内部定义,对外统一暴露 loadAndSaveFoodVector 接口
- 更新 InitActivity、MainActivity 调用方,移除重复的 onPageFinish 回调
- SensorScaleUtils.zero() 新增 isShowToastRemind 可选参数,支持按需显示标定成功提示

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-07 09:25:03 +08:00
co-authored by Claude Sonnet 4.6
parent ab0d9b35f5
commit a45690d3be
4 changed files with 76 additions and 73 deletions
@@ -13,10 +13,7 @@ import com.sw.dualscreen.dialog.RemindDialog
import com.sw.dualscreen.ext.clickWithDebounce import com.sw.dualscreen.ext.clickWithDebounce
import com.sw.dualscreen.ext.invisible import com.sw.dualscreen.ext.invisible
import com.sw.dualscreen.ext.visible import com.sw.dualscreen.ext.visible
import com.sw.dualscreen.model.response.FoodVector
import com.sw.dualscreen.objbox.Food
import com.sw.dualscreen.objbox.FoodModule import com.sw.dualscreen.objbox.FoodModule
import com.sw.dualscreen.objbox.ObjectBox
import com.sw.dualscreen.utils.FoodVectorTool import com.sw.dualscreen.utils.FoodVectorTool
import com.sw.dualscreen.utils.L import com.sw.dualscreen.utils.L
import com.sw.dualscreen.utils.NetworkUtils import com.sw.dualscreen.utils.NetworkUtils
@@ -42,43 +39,11 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
} }
private fun getCollectedFoodVector(block: () -> Unit) { private fun getCollectedFoodVector(block: () -> Unit) {
// userViewModel.getCollectedFoodVector( FoodVectorTool.loadAndSaveFoodVector(
// param = mutableMapOf( userViewModel = userViewModel,
// "pageNum" to "$pageNum", lifecycleScope = lifecycleScope,
// "pageSize" to "$PAGE_SIZE", successBlock = { block() },
// "version" to GlobalData.foodModelVersion failureBlock = { getVectorErrorDialog() }
// )
// ) { items ->
// if (items == null) {
// getVectorErrorDialog()
// return@getCollectedFoodVector
// }
// val list = items
// if (pageNum == 1 && list.isEmpty()) {
// block()
// return@getCollectedFoodVector
// }
// if (list.size >= PAGE_SIZE) {
// saveFoodVector(list)
// pageNum++
// getCollectedFoodVector(block)
// return@getCollectedFoodVector
// }
// if (list.isNotEmpty()) {
// saveFoodVector(list)
// block()
// }
// }
FoodVectorTool.getFoodVector(
userViewModel,
onPageFinish = { list ->
list?.let { FoodVectorTool.saveFoodVector(lifecycleScope,list) }
}, successBlock = {
block()
}, failureBlock = {
getVectorErrorDialog()
}
) )
} }
@@ -215,19 +215,16 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
lifecycleScope.launch { lifecycleScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
FaceServer.getInstance().clearAllFaces() FaceServer.getInstance().clearAllFaces()
}
withContext(Dispatchers.IO) {
ObjectBox.removeAll() ObjectBox.removeAll()
} }
FoodVectorTool.getFoodVector( FoodVectorTool.loadAndSaveFoodVector(
userViewModel = viewModel, userViewModel = viewModel,
onPageFinish = { list -> lifecycleScope = lifecycleScope,
list?.let { FoodVectorTool.saveFoodVector(lifecycleScope,list) }
},
successBlock = { successBlock = {
viewModel.getUserFaceCache(pageNo = 1) viewModel.getUserFaceCache(pageNo = 1)
}, failureBlock = { },
hideWaitingDialog() // 显示提示信息 failureBlock = {
hideWaitingDialog()
ToastUtils.showToast(it) ToastUtils.showToast(it)
} }
) )
@@ -129,13 +129,15 @@ object SensorScaleUtils {
/** /**
* 零位标定 * 零位标定
*/ */
fun zero() { fun zero(isShowToastRemind: Boolean = false) {
Timber.d("zero isOpened = $isOpened, mSensorScale = $mSensorScale") Timber.d("zero isOpened = $isOpened, mSensorScale = $mSensorScale")
if (!isOpened) return if (!isOpened) return
mSensorScale?.zero { mSensorScale?.zero {
isZero = true isZero = true
Timber.d("zero 零位标定操作成功") Timber.d("zero 零位标定操作成功")
ToastUtils.showToast("零位标定操作成功") if (isShowToastRemind) {
ToastUtils.showToast("零位标定操作成功")
}
} }
} }
@@ -1,8 +1,7 @@
package com.sw.dualscreen.utils package com.sw.dualscreen.utils
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.LifecycleCoroutineScope
import com.sw.dualscreen.GlobalData import com.sw.dualscreen.GlobalData
import com.sw.dualscreen.activity.InitActivity
import com.sw.dualscreen.model.response.FoodVector import com.sw.dualscreen.model.response.FoodVector
import com.sw.dualscreen.objbox.Food import com.sw.dualscreen.objbox.Food
import com.sw.dualscreen.objbox.ObjectBox import com.sw.dualscreen.objbox.ObjectBox
@@ -10,15 +9,50 @@ import com.sw.dualscreen.viewmodel.UserViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
object FoodVectorTool { object FoodVectorTool {
var pageNum: Int = 1
const val PAGE_SIZE: Int = InitActivity.PAGE_SIZE
fun getFoodVector( /** 每页拉取条数 */
private const val PAGE_SIZE = 100
/**
* 统一入口:分页拉取所有食物向量数据并保存到本地 ObjectBox
*
* @param userViewModel 请求 ViewModel
* @param lifecycleScope 协程作用域(用于保存操作)
* @param successBlock 全部数据拉取并保存成功后回调
* @param failureBlock 任意一次请求失败时回调,参数为错误信息
*/
fun loadAndSaveFoodVector(
userViewModel: UserViewModel, userViewModel: UserViewModel,
onPageFinish:(List<FoodVector>?)-> Unit, lifecycleScope: LifecycleCoroutineScope,
successBlock: () -> Unit, successBlock: () -> Unit,
failureBlock: (String) -> Unit failureBlock: (String) -> Unit
): Unit { ) {
// 每次调用从第 1 页开始,避免单例状态污染
fetchPage(
pageNum = 1,
userViewModel = userViewModel,
lifecycleScope = lifecycleScope,
successBlock = successBlock,
failureBlock = failureBlock
)
}
/**
* 递归分页请求(内部方法,外部不感知分页细节)
*
* @param pageNum 当前页码
* @param userViewModel 请求 ViewModel
* @param lifecycleScope 协程作用域
* @param successBlock 成功回调
* @param failureBlock 失败回调
*/
private fun fetchPage(
pageNum: Int,
userViewModel: UserViewModel,
lifecycleScope: LifecycleCoroutineScope,
successBlock: () -> Unit,
failureBlock: (String) -> Unit
) {
userViewModel.getCollectedFoodVector( userViewModel.getCollectedFoodVector(
param = mutableMapOf( param = mutableMapOf(
"pageNum" to "$pageNum", "pageNum" to "$pageNum",
@@ -30,30 +64,36 @@ object FoodVectorTool {
failureBlock("未查询到向量数据") failureBlock("未查询到向量数据")
return@getCollectedFoodVector return@getCollectedFoodVector
} }
val list = items // 第一页为空,视为成功(数据库无数据)
if (pageNum == 1 && list.isEmpty()) { if (pageNum == 1 && items.isEmpty()) {
//接口成功无数据,暂时认为成功
successBlock() successBlock()
return@getCollectedFoodVector return@getCollectedFoodVector
} }
if (list.size >= PAGE_SIZE) { // 有数据则保存
onPageFinish(list) if (items.isNotEmpty()) {
pageNum++ saveFoodVector(lifecycleScope, items)
getFoodVector(userViewModel, onPageFinish, successBlock, failureBlock)
return@getCollectedFoodVector
} }
if (list.isNotEmpty()) { // 数据量达到一页上限,继续拉取下一页
onPageFinish(list) if (items.size >= PAGE_SIZE) {
fetchPage(pageNum + 1, userViewModel, lifecycleScope, successBlock, failureBlock)
} else {
successBlock()
} }
successBlock()
} }
} }
fun saveFoodVector(lifecycleScope: androidx.lifecycle.LifecycleCoroutineScope ,list: List<FoodVector>) { /**
* 将向量数据列表保存到 ObjectBox 本地数据库
*
* @param lifecycleScope 协程作用域
* @param list 待保存的食物向量数据列表
*/
private fun saveFoodVector(lifecycleScope: LifecycleCoroutineScope, list: List<FoodVector>) {
lifecycleScope.launch { lifecycleScope.launch {
val vectorList = list.map { vt -> val vectorList = list.map { vt ->
val foodVector = vt.foodVector?.removeSurrounding("[", "]")?.split(",") // 解析向量字符串 "[0.1,0.2,...]" 为 FloatArray
?.map { it.toFloatOrNull() ?: 0.0f }?.toFloatArray() val foodVector = vt.foodVector?.removeSurrounding("[", "]")
?.split(",")?.map { it.toFloatOrNull() ?: 0.0f }?.toFloatArray()
Food( Food(
collectId = vt.id, collectId = vt.id,
foodId = vt.foodId, foodId = vt.foodId,
@@ -65,5 +105,4 @@ object FoodVectorTool {
ObjectBox.putAll(vectorList) ObjectBox.putAll(vectorList)
} }
} }
}
}