refactor(activity): 全面优化 LoginByFaceActivity 代码质量

- 移除静态 instance 引用,消除潜在内存泄漏风险
- Observer {} 显式包装统一改为 Kotlin lambda 写法
- 匿名 Debouncer 改为成员变量 openDebouncer(修复防抖失效 bug)
- Thread.start() 替换为 lifecycleScope.launch(Dispatchers.IO)
- Arrays.fill() 替换为 Kotlin ByteArray.fill(),移除 Java import
- 清理死字段:regDebouncer、faceUseSwitch、faceFailThreshold、tempUserId
- Java getter 语法统一改为 Kotlin 属性访问
- 移除 protected override、无效 handleLoginSuccess override 等冗余代码
- 顶部添加 @file:Suppress("DEPRECATION") 统一处理废弃 API 警告

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 10:18:39 +08:00
co-authored by Claude Sonnet 4.6
parent 08f1963054
commit dca9496bee
@@ -1,3 +1,5 @@
@file:Suppress("DEPRECATION")
package com.sw.platecabinet.activity package com.sw.platecabinet.activity
import android.Manifest import android.Manifest
@@ -10,10 +12,8 @@ import android.util.DisplayMetrics
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.ImageView
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.arcsoft.face.ErrorInfo import com.arcsoft.face.ErrorInfo
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@@ -58,12 +58,13 @@ import com.sw.platecabinet.utils.PlateUtils
import com.sw.platecabinet.utils.RegisterCallbackHandler import com.sw.platecabinet.utils.RegisterCallbackHandler
import com.sw.platecabinet.utils.SoundPoolUtil import com.sw.platecabinet.utils.SoundPoolUtil
import com.sw.platecabinet.utils.SpTool import com.sw.platecabinet.utils.SpTool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.util.Arrays
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
/** /**
@@ -89,22 +90,13 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
companion object { companion object {
private const val TAG = "LoginByFaceActivity" private const val TAG = "LoginByFaceActivity"
private var instance: LoginByFaceActivity? = null
private const val COLLECT_SUCCESS = "collect_success" private const val COLLECT_SUCCESS = "collect_success"
private const val RECOGNIZE_SUCCESS = "recognize_success" private const val RECOGNIZE_SUCCESS = "recognize_success"
private const val NO_PLATE_REMIND = "no_plate_remind"; private const val NO_PLATE_REMIND = "no_plate_remind"
fun goInitActivity() {
instance?.goInitActivity()
} }
}
// private var logList: MutableList<String> = mutableListOf()
// private val logAdapter by lazy { LogAdapter(logList) }
override fun initialize() { override fun initialize() {
instance = this
userViewModel.activeEngine() userViewModel.activeEngine()
initArcViewModel() initArcViewModel()
@@ -174,7 +166,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
private var rgbFaceRectTransformer: FaceRectTransformer? = null private var rgbFaceRectTransformer: FaceRectTransformer? = null
private val livenessType = LivenessType.RGB private val livenessType = LivenessType.RGB
private var openRectInfoDraw = false private var openRectInfoDraw = false
private val regDebouncer = Debouncer(3000)
private val leftDebouncer = Debouncer(30*1000) private val leftDebouncer = Debouncer(30*1000)
private var registerHandler: RegisterCallbackHandler? = null private var registerHandler: RegisterCallbackHandler? = null
@@ -213,7 +205,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
} }
} }
} }
recognizeViewModel.ftInitCode.observe(this, Observer { ftInitCode: Int? -> recognizeViewModel.ftInitCode.observe(this) { ftInitCode: Int? ->
if (ftInitCode != ErrorInfo.MOK) { if (ftInitCode != ErrorInfo.MOK) {
val error: String? = getString( val error: String? = getString(
R.string.specific_engine_init_failed, "ftEngine", R.string.specific_engine_init_failed, "ftEngine",
@@ -222,8 +214,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
Timber.e("ftInitCode observe = $error") Timber.e("ftInitCode observe = $error")
ToastUtils.showToast(error) ToastUtils.showToast(error)
} }
}) }
recognizeViewModel.frInitCode.observe(this, Observer { frInitCode: Int? -> recognizeViewModel.frInitCode.observe(this) { frInitCode: Int? ->
if (frInitCode != ErrorInfo.MOK) { if (frInitCode != ErrorInfo.MOK) {
val error: String? = getString( val error: String? = getString(
R.string.specific_engine_init_failed, "frEngine", R.string.specific_engine_init_failed, "frEngine",
@@ -232,8 +224,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
Timber.e("frInitCode observe = $error") Timber.e("frInitCode observe = $error")
ToastUtils.showToast(error) ToastUtils.showToast(error)
} }
}) }
recognizeViewModel.flInitCode.observe(this, Observer { flInitCode: Int? -> recognizeViewModel.flInitCode.observe(this) { flInitCode: Int? ->
if (flInitCode != ErrorInfo.MOK) { if (flInitCode != ErrorInfo.MOK) {
val error: String? = getString( val error: String? = getString(
R.string.specific_engine_init_failed, "flEngine", R.string.specific_engine_init_failed, "flEngine",
@@ -242,30 +234,25 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
Timber.e("flInitCode observe = $error") Timber.e("flInitCode observe = $error")
ToastUtils.showToast(error) ToastUtils.showToast(error)
} }
})
recognizeViewModel.recognizeConfiguration
.observe(this, Observer { recognizeConfiguration: RecognizeConfiguration? ->
Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
})
recognizeViewModel.recognizeNotice.observe(this, Observer { notice: String? ->
Timber.i("recognizeNotice observe notice = $notice")
})
recognizeViewModel.recognizeUserId.observe(this, Observer { result: CompareResult? ->
loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}")
loadFaceRecognizeResult3(result)
})
recognizeViewModel.drawRectInfoText.observe(this, Observer { info ->
Timber.i("drawRectInfoText observe info = $info")
})
} }
/** recognizeViewModel.recognizeConfiguration
* 人脸识别开关,true-loadFaceRecognizeResult2false-loadFaceRecognizeResult .observe(this) { recognizeConfiguration: RecognizeConfiguration? ->
*/ Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
private var faceUseSwitch = true }
recognizeViewModel.recognizeNotice.observe(this) { notice: String? ->
Timber.i("recognizeNotice observe notice = $notice")
}
recognizeViewModel.recognizeUserId.observe(this) { result: CompareResult? ->
loadLogInfo("collectFace,recognizeUserId observe userId = ${result?.faceEntity?.userName}")
loadFaceRecognizeResult3(result)
}
recognizeViewModel.drawRectInfoText.observe(this) { info ->
Timber.i("drawRectInfoText observe info = $info")
}
}
/** /**
* 识别失败重试次数 * 识别失败重试次数
@@ -277,11 +264,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
*/ */
private val maxFailCount = 5 private val maxFailCount = 5
/**
* 人脸失败阈值
*/
private val faceFailThreshold = 0.3
/** /**
* 配置阈值,默认值为0.8 * 配置阈值,默认值为0.8
*/ */
@@ -295,8 +277,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
binding.tvSimilarValue.text = "$similar${retryFailCount}" binding.tvSimilarValue.text = "$similar${retryFailCount}"
loadLogInfo( loadLogInfo(
"collectFace,recognizeUserId 阈值 success=$faceSuccessThreshold, " + "collectFace,recognizeUserId 阈值 success=$faceSuccessThreshold, similar=$similar, retry=$retryFailCount"
"fail=$faceFailThreshold, similar=$similar, retry=$retryFailCount"
) )
if (similar >= faceSuccessThreshold) { if (similar >= faceSuccessThreshold) {
val userId = result.faceEntity?.userName val userId = result.faceEntity?.userName
@@ -309,7 +290,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
return return
} }
// if (similar <= faceFailThreshold || retryFailCount >= maxFailCount) {
if (retryFailCount >= maxFailCount) { if (retryFailCount >= maxFailCount) {
loadLogInfo( loadLogInfo(
"collectFace,recognizeUserId 识别失败,开始采集 similar=$similar, " + "collectFace,recognizeUserId 识别失败,开始采集 similar=$similar, " +
@@ -404,7 +384,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
item.updateTime = DateTimeUtils.getDateTimeString() item.updateTime = DateTimeUtils.getDateTimeString()
item.plateNumber = null item.plateNumber = null
plateEnable = false plateEnable = false
Debouncer(5000).debounce { openDebouncer.debounce {
//打开柜子 //打开柜子
PlateUtils.open(this, item.equipmentBoxCode, false) { PlateUtils.open(this, item.equipmentBoxCode, false) {
goInitActivity() goInitActivity()
@@ -416,20 +396,19 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
SpTool.savePlateData(items) SpTool.savePlateData(items)
} }
} }
private val openDebouncer by lazy { Debouncer(5000) }
// private val debouncer by lazy { Debouncer(3000) }
private fun initArcView() { private fun initArcView() {
//在布局结束后才做初始化操作 //在布局结束后才做初始化操作
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this) binding.dualCameraTexturePreviewRgb.viewTreeObserver.addOnGlobalLayoutListener(this)
recognizeViewModel.getCompareResultList().getValue() val list = recognizeViewModel.getCompareResultList().getValue()
loadLogInfo("initArcView:list.size=${list?.size}")
} }
override fun onDestroy() { override fun onDestroy() {
loadLogInfo("onDestroy") loadLogInfo("onDestroy")
instance = null
if (rgbCameraHelper != null) { if (rgbCameraHelper != null) {
rgbCameraHelper!!.release() rgbCameraHelper?.release()
rgbCameraHelper = null rgbCameraHelper = null
} }
@@ -474,7 +453,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
layoutParams.width = (measuredHeight * ratio).toInt() layoutParams.width = (measuredHeight * ratio).toInt()
} }
if (scale < 1f) { if (scale < 1f) {
val rgbParam = rgbPreview.getLayoutParams() val rgbParam = rgbPreview.layoutParams
layoutParams.width = (rgbParam.width * scale).toInt() layoutParams.width = (rgbParam.width * scale).toInt()
layoutParams.height = (rgbParam.height * scale).toInt() layoutParams.height = (rgbParam.height * scale).toInt()
} else { } else {
@@ -510,8 +489,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
isMirror: Boolean isMirror: Boolean
) { ) {
loadLogInfo("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation") loadLogInfo("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
runOnUiThread({ runOnUiThread {
val previewSizeRgb = camera.getParameters().getPreviewSize() val previewSizeRgb = camera.parameters.previewSize
val layoutParams = adjustPreviewViewSize( val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb, binding.dualCameraTexturePreviewRgb,
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView, binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
@@ -545,7 +524,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
recognizeViewModel.onRgbCameraOpened(camera) recognizeViewModel.onRgbCameraOpened(camera)
recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer) recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
}) }
} }
@RequiresApi(api = Build.VERSION_CODES.Q) @RequiresApi(api = Build.VERSION_CODES.Q)
@@ -607,8 +586,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
.previewOn(binding.dualCameraTexturePreviewRgb) .previewOn(binding.dualCameraTexturePreviewRgb)
.cameraListener(cameraListener) .cameraListener(cameraListener)
.build() .build()
rgbCameraHelper!!.init() rgbCameraHelper?.init()
rgbCameraHelper!!.start() rgbCameraHelper?.start()
} }
/** /**
@@ -634,7 +613,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
override fun onGlobalLayout() { override fun onGlobalLayout() {
loadLogInfo("onGlobalLayout") loadLogInfo("onGlobalLayout")
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this) binding.dualCameraTexturePreviewRgb.viewTreeObserver.removeOnGlobalLayoutListener(this)
// 请求权限 // 请求权限
checkCameraPermission() checkCameraPermission()
} }
@@ -644,7 +623,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) { if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
loadLogInfo("collectFace,resumeCamera: ") loadLogInfo("collectFace,resumeCamera: ")
isRecognition = true isRecognition = true
rgbCameraHelper!!.start() rgbCameraHelper?.start()
} else { } else {
//recognizeViewModel.onPreviewFrame(ByteArray(1382400), true) //recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
@@ -669,9 +648,11 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
// Y 分量(亮度)设为 0(全黑),UV 分量设为 128(默认值) // Y 分量(亮度)设为 0(全黑),UV 分量设为 128(默认值)
// 1. 填充 Y 分量(前 frameSize 个字节) // 1. 填充 Y 分量(前 frameSize 个字节)
Arrays.fill(nv21Data, 0, frameSize, 0.toByte()) // Arrays.fill(nv21Data, 0, frameSize, 0.toByte())
nv21Data.fill(0, 0, frameSize)
// 2. 填充 UV 分量(后 frameSize/2 个字节) // 2. 填充 UV 分量(后 frameSize/2 个字节)
Arrays.fill(nv21Data, frameSize, nv21Data.size, 128.toByte()) // Arrays.fill(nv21Data, frameSize, nv21Data.size, 128.toByte())
nv21Data.fill(128.toByte(), frameSize, nv21Data.size)
return nv21Data return nv21Data
} }
@@ -680,10 +661,10 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
val emptyFrame = createBlackNV21Frame(1920, 1080) val emptyFrame = createBlackNV21Frame(1920, 1080)
// 传递“空帧” recognizeViewModel.onPreviewFrame(emptyFrame, true) // 传递“空帧” recognizeViewModel.onPreviewFrame(emptyFrame, true)
/** // /**
* 临时用户id // * 临时用户id
*/ // */
private var tempUserId: String = "" // private var tempUserId: String = ""
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
try { try {
@@ -714,7 +695,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
isCollectFace = false isCollectFace = false
plateEnable = true plateEnable = true
collectCount = 0 collectCount = 0
tempUserId = "${System.currentTimeMillis()}" // tempUserId = "${System.currentTimeMillis()}"
resumeCamera() resumeCamera()
registerHandler = RegisterCallbackHandler() registerHandler = RegisterCallbackHandler()
@@ -741,7 +722,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
private var isFirstOpen = true private var isFirstOpen = true
protected override fun onPause() { override fun onPause() {
pauseCamera() pauseCamera()
super.onPause() super.onPause()
CountDownManager.cancelCountDown() CountDownManager.cancelCountDown()
@@ -752,11 +733,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
// recognizeViewModel.onPreviewFrame(emptyFrame, true) // recognizeViewModel.onPreviewFrame(emptyFrame, true)
} }
override fun handleLoginSuccess(equipmentUserInfo: EquipmentUserInfo, isAdmin: Boolean) {
super.handleLoginSuccess(equipmentUserInfo, isAdmin)
// goInitActivity()
}
private fun judgeRecognizeState(): Boolean { private fun judgeRecognizeState(): Boolean {
val rectColor = binding.dualCameraFaceRectView.rectColor val rectColor = binding.dualCameraFaceRectView.rectColor
if (rectColor == RecognizeColor.COLOR_SUCCESS && currentUserId.isNullOrBlank().not() && facePreviewInfoList.isNullOrEmpty().not()) { if (rectColor == RecognizeColor.COLOR_SUCCESS && currentUserId.isNullOrBlank().not() && facePreviewInfoList.isNullOrEmpty().not()) {
@@ -823,7 +799,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
loadLogInfo("collectFace,sendFaceData: ----------12,发送人脸数据成功但接口返回为null,faceData=${faceData}") loadLogInfo("collectFace,sendFaceData: ----------12,发送人脸数据成功但接口返回为null,faceData=${faceData}")
return@addUserFace return@addUserFace
} }
Thread { lifecycleScope.launch(Dispatchers.IO) {
saveFaceInfo(faceModel) { saveFaceInfo(faceModel) {
runOnUiThread { runOnUiThread {
Glide.with(this@LoginByFaceActivity).load(imageFile).into(binding.ivCollectPhoto) Glide.with(this@LoginByFaceActivity).load(imageFile).into(binding.ivCollectPhoto)