refactor(base): 优化协程作用域和空安全处理
- 将自定义CoroutineScope替换为lifecycleScope以避免内存泄漏 - 添加runCatching包装器处理潜在异常并记录错误日志 - 使用安全调用操作符替代强制调用防止空指针异常 - 在CountDownTimer中正确管理生命周期以避免内存泄漏 - 修复设备信息获取中的数组越界风险 - 统一异步操作的线程切换方式 - 保护Fragment中的_binding访问权限 - 改进相机助手对象引用的安全性检查
This commit is contained in:
@@ -30,10 +30,10 @@ import com.sw.platecabinet.utils.IntervalExecutor
|
|||||||
import com.sw.platecabinet.utils.SpTool
|
import com.sw.platecabinet.utils.SpTool
|
||||||
import com.sw.platecabinet.view.CustomLoadingDialog
|
import com.sw.platecabinet.view.CustomLoadingDialog
|
||||||
import com.sw.platecabinet.viewmodel.UserViewModel
|
import com.sw.platecabinet.viewmodel.UserViewModel
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,8 +142,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
onRightDoubleClick()
|
onRightDoubleClick()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
val scope = CoroutineScope(Dispatchers.Main)
|
timeJob = lifecycleScope.launch {
|
||||||
timeJob = scope.launch {
|
|
||||||
DateTimeUtils.realTimeChineseDateFlow()
|
DateTimeUtils.realTimeChineseDateFlow()
|
||||||
.collect { date ->
|
.collect { date ->
|
||||||
it.tvRightTime.text = date
|
it.tvRightTime.text = date
|
||||||
@@ -347,7 +346,9 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
val alreadyExists = featureMatches.any { it }
|
val alreadyExists = featureMatches.any { it }
|
||||||
if (!alreadyExists) {
|
if (!alreadyExists) {
|
||||||
//当前特征不存在,保存
|
//当前特征不存在,保存
|
||||||
faceList.add(getNewFaceEntity(model))
|
runCatching { getNewFaceEntity(model) }
|
||||||
|
.onSuccess { faceList.add(it) }
|
||||||
|
.onFailure { Timber.e(it, "人脸特征解码失败: userId=${model.userId}") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -378,13 +379,13 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun clearAllFace(block: () -> Unit) {
|
fun clearAllFace(block: () -> Unit) {
|
||||||
Thread {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val faceDao = FaceDatabase.getInstance(App.getContext()).faceDao()
|
val faceDao = FaceDatabase.getInstance(App.getContext()).faceDao()
|
||||||
faceDao.deleteAll()
|
faceDao.deleteAll()
|
||||||
faceDao.resetId()
|
faceDao.resetId()
|
||||||
recognizeViewModel.refreshFaceList()
|
recognizeViewModel.refreshFaceList()
|
||||||
runOnUiThread { block() }
|
withContext(Dispatchers.Main) { block() }
|
||||||
}.start()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var permissionCallback: ((isGranted: Boolean) -> Unit)? = null
|
private var permissionCallback: ((isGranted: Boolean) -> Unit)? = null
|
||||||
|
|||||||
@@ -238,7 +238,10 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
return@setCallback
|
return@setCallback
|
||||||
}
|
}
|
||||||
if (this.userFaceInfo == null && isFitData(userFaceInfo)) {
|
if (this.userFaceInfo == null && isFitData(userFaceInfo)) {
|
||||||
val headBmp = userFaceInfo.headBmp
|
val headBmp = userFaceInfo.headBmp ?: run {
|
||||||
|
loadLogInfo("collectFace,isFitData:headBmp 为 null,跳过")
|
||||||
|
return@setCallback
|
||||||
|
}
|
||||||
if (headBmp.width <= 200 || headBmp.height <= 200) {
|
if (headBmp.width <= 200 || headBmp.height <= 200) {
|
||||||
loadLogInfo("collectFace,isFitData:拿到人脸提取特征数据照片过小w:${headBmp.width},h:${headBmp.height}-----------")
|
loadLogInfo("collectFace,isFitData:拿到人脸提取特征数据照片过小w:${headBmp.width},h:${headBmp.height}-----------")
|
||||||
binding.llCollectButton.visible()
|
binding.llCollectButton.visible()
|
||||||
@@ -253,30 +256,30 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
recognizeViewModel.ftInitCode.observe(this) { ftInitCode: Int? ->
|
recognizeViewModel.ftInitCode.observe(this) { ftInitCode: Int? ->
|
||||||
if (ftInitCode != ErrorInfo.MOK) {
|
if (ftInitCode != null && 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",
|
||||||
ftInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(ftInitCode!!)
|
ftInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(ftInitCode)
|
||||||
)
|
)
|
||||||
Timber.e("ftInitCode observe = $error")
|
Timber.e("ftInitCode observe = $error")
|
||||||
ToastUtils.showToast(error)
|
ToastUtils.showToast(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recognizeViewModel.frInitCode.observe(this) { frInitCode: Int? ->
|
recognizeViewModel.frInitCode.observe(this) { frInitCode: Int? ->
|
||||||
if (frInitCode != ErrorInfo.MOK) {
|
if (frInitCode != null && 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",
|
||||||
frInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(frInitCode!!)
|
frInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(frInitCode)
|
||||||
)
|
)
|
||||||
Timber.e("frInitCode observe = $error")
|
Timber.e("frInitCode observe = $error")
|
||||||
ToastUtils.showToast(error)
|
ToastUtils.showToast(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recognizeViewModel.flInitCode.observe(this) { flInitCode: Int? ->
|
recognizeViewModel.flInitCode.observe(this) { flInitCode: Int? ->
|
||||||
if (flInitCode != ErrorInfo.MOK) {
|
if (flInitCode != null && 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",
|
||||||
flInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(flInitCode!!)
|
flInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(flInitCode)
|
||||||
)
|
)
|
||||||
Timber.e("flInitCode observe = $error")
|
Timber.e("flInitCode observe = $error")
|
||||||
ToastUtils.showToast(error)
|
ToastUtils.showToast(error)
|
||||||
@@ -615,7 +618,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
|
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
|
||||||
Timber.i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
|
Timber.i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
|
||||||
if (rgbFaceRectTransformer != null) {
|
if (rgbFaceRectTransformer != null) {
|
||||||
rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
|
rgbFaceRectTransformer?.cameraDisplayOrientation = displayOrientation
|
||||||
}
|
}
|
||||||
Timber.i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
|
Timber.i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
|
||||||
}
|
}
|
||||||
@@ -672,10 +675,11 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
|||||||
|
|
||||||
private fun resumeCamera() {
|
private fun resumeCamera() {
|
||||||
// isRecognition = true
|
// isRecognition = true
|
||||||
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
|
val helper = rgbCameraHelper
|
||||||
|
if (helper != null && helper.isStopped) {
|
||||||
loadLogInfo("collectFace,resumeCamera: ")
|
loadLogInfo("collectFace,resumeCamera: ")
|
||||||
isRecognition = true
|
isRecognition = true
|
||||||
rgbCameraHelper!!.start()
|
helper.start()
|
||||||
} else {
|
} else {
|
||||||
//recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
//recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import com.sw.platecabinet.view.CustomLoadingDialog
|
|||||||
abstract class BaseFragment<VB : ViewBinding>(
|
abstract class BaseFragment<VB : ViewBinding>(
|
||||||
private val bindingInflater: (inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> VB
|
private val bindingInflater: (inflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> VB
|
||||||
) : Fragment() {
|
) : Fragment() {
|
||||||
private var _binding: VB? = null
|
protected var _binding: VB? = null
|
||||||
protected val binding get() = _binding!!
|
protected val binding get() = _binding!!
|
||||||
private var mDialogWaiting: CustomLoadingDialog? = null
|
private var mDialogWaiting: CustomLoadingDialog? = null
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import timber.log.Timber
|
|||||||
class PlateCabinetFullFragment :
|
class PlateCabinetFullFragment :
|
||||||
BaseFragment<FragmentPlateCabinetFullBinding>(FragmentPlateCabinetFullBinding::inflate) {
|
BaseFragment<FragmentPlateCabinetFullBinding>(FragmentPlateCabinetFullBinding::inflate) {
|
||||||
private var totalTimeInMillis: Long = Constants.AUTO_CLOSE_TIME * 1000
|
private var totalTimeInMillis: Long = Constants.AUTO_CLOSE_TIME * 1000
|
||||||
|
private var countDownTimer: CountDownTimer? = null
|
||||||
|
|
||||||
override fun initialize() {
|
override fun initialize() {
|
||||||
Timber.d("initialize")
|
Timber.d("initialize")
|
||||||
@@ -18,15 +19,22 @@ class PlateCabinetFullFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun initCountTime() {
|
fun initCountTime() {
|
||||||
object : CountDownTimer(totalTimeInMillis, 1000) {
|
countDownTimer = object : CountDownTimer(totalTimeInMillis, 1000) {
|
||||||
override fun onTick(millisUntilFinished: Long) {
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
binding.tvAutoClose.text = "${(millisUntilFinished / 1000).toInt() + 1}秒后返回主屏"
|
_binding?.tvAutoClose?.text = "${(millisUntilFinished / 1000).toInt() + 1}秒后返回主屏"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
activity?.finish()
|
activity?.finish()
|
||||||
}
|
}
|
||||||
}.start()
|
}
|
||||||
|
countDownTimer?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
countDownTimer?.cancel()
|
||||||
|
countDownTimer = null
|
||||||
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,7 @@ class PlateOpenFragment :
|
|||||||
// private var isAdmin: Boolean = false
|
// private var isAdmin: Boolean = false
|
||||||
|
|
||||||
private var isScanCode = false
|
private var isScanCode = false
|
||||||
|
private var countDownTimer: CountDownTimer? = null
|
||||||
companion object {
|
companion object {
|
||||||
const val IS_SCAN_CODE = "isScanCode"
|
const val IS_SCAN_CODE = "isScanCode"
|
||||||
// @JvmStatic
|
// @JvmStatic
|
||||||
@@ -75,19 +76,23 @@ class PlateOpenFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun initCountTime() {
|
fun initCountTime() {
|
||||||
object : CountDownTimer(totalTimeInMillis, 1000) {
|
countDownTimer = object : CountDownTimer(totalTimeInMillis, 1000) {
|
||||||
override fun onTick(millisUntilFinished: Long) {
|
override fun onTick(millisUntilFinished: Long) {
|
||||||
binding.tvAutoClose.text = ((millisUntilFinished / 1000).toInt() + 1).toString()
|
_binding?.tvAutoClose?.text = ((millisUntilFinished / 1000).toInt() + 1).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFinish() {
|
override fun onFinish() {
|
||||||
|
val ctx = context ?: return
|
||||||
activity?.finish()
|
activity?.finish()
|
||||||
// if (!isAdmin) {
|
ctx.startActivity(Intent(ctx, InitActivity::class.java))
|
||||||
val intent = Intent(context, InitActivity::class.java)
|
|
||||||
startActivity(intent)
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}.start()
|
}
|
||||||
|
countDownTimer?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
countDownTimer?.cancel()
|
||||||
|
countDownTimer = null
|
||||||
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ class CrashHandler private constructor(private val context: Context) :
|
|||||||
// 设备信息
|
// 设备信息
|
||||||
sb.append("Vendor: ${Build.MANUFACTURER}\n")
|
sb.append("Vendor: ${Build.MANUFACTURER}\n")
|
||||||
sb.append("Model: ${Build.MODEL}\n")
|
sb.append("Model: ${Build.MODEL}\n")
|
||||||
sb.append("CPU ABI: ${Build.SUPPORTED_ABIS[0]}\n")
|
sb.append("CPU ABI: ${Build.SUPPORTED_ABIS.firstOrNull() ?: "unknown"}\n")
|
||||||
|
|
||||||
// 其他信息
|
// 其他信息
|
||||||
sb.append("Locale: ${Locale.getDefault()}\n")
|
sb.append("Locale: ${Locale.getDefault()}\n")
|
||||||
|
|||||||
Reference in New Issue
Block a user