识别成功未提示及采集时间长问题优化
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package com.sw.platecabinet.utils
|
||||
|
||||
import android.os.CountDownTimer
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Message
|
||||
import android.util.Log
|
||||
|
||||
/**
|
||||
* 无状态残留的CountDownTimer封装(解决时间残留问题)
|
||||
* @param millisInFuture 总时长(毫秒)
|
||||
* @param countDownInterval 倒计时间隔(毫秒)
|
||||
* @param onTick 倒计时回调(剩余毫秒)
|
||||
* @param onFinish 结束回调
|
||||
*/
|
||||
class SafeCountDownTimer(
|
||||
private val millisInFuture: Long,
|
||||
private val countDownInterval: Long,
|
||||
private val onTick: (Long) -> Unit,
|
||||
private val onFinish: () -> Unit
|
||||
) {
|
||||
// 独立Handler,避免与其他CountDownTimer复用消息队列
|
||||
private val handler = object : Handler(Looper.getMainLooper()) {
|
||||
override fun handleMessage(msg: Message) {
|
||||
synchronized(this@SafeCountDownTimer) {
|
||||
// 仅当timer未取消时处理消息,避免残留消息触发
|
||||
if (!isCancelled) {
|
||||
super.handleMessage(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 核心:标记是否已取消,避免残留消息触发
|
||||
@Volatile
|
||||
private var isCancelled = false
|
||||
// 实际的CountDownTimer实例
|
||||
private var timer: CountDownTimer? = null
|
||||
|
||||
/**
|
||||
* 启动倒计时(启动前自动清空旧状态)
|
||||
*/
|
||||
fun start() {
|
||||
// 第一步:强制取消旧实例+清空所有残留消息(核心!)
|
||||
cancel()
|
||||
|
||||
// 第二步:重置取消标记
|
||||
isCancelled = false
|
||||
|
||||
// 第三步:新建CountDownTimer实例,绑定独立Handler
|
||||
timer = object : CountDownTimer(millisInFuture, countDownInterval) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
// 双重校验:仅未取消时触发回调
|
||||
if (!isCancelled) {
|
||||
onTick.invoke(millisUntilFinished)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
if (!isCancelled) {
|
||||
onFinish.invoke()
|
||||
// 结束后主动清空,避免残留
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 第四步:启动新实例
|
||||
timer?.start()
|
||||
Log.d("SafeCountDownTimer", "倒计时启动,总时长:$millisInFuture 毫秒")
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消倒计时(彻底清空所有状态)
|
||||
*/
|
||||
fun cancel() {
|
||||
// 标记为已取消,拦截后续消息
|
||||
isCancelled = true
|
||||
|
||||
// 取消timer实例
|
||||
timer?.cancel()
|
||||
timer = null
|
||||
|
||||
// 清空Handler所有残留消息(核心:解决旧消息触发问题)
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
|
||||
Log.d("SafeCountDownTimer", "倒计时已取消,清空所有残留状态")
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 封装成工具类,方便全局调用 ==========
|
||||
object CountDownManager {
|
||||
// 保存当前倒计时实例,避免重复启动
|
||||
private var currentTimer: SafeCountDownTimer? = null
|
||||
|
||||
/**
|
||||
* 启动/重启倒计时(适配人脸识别防抖场景)
|
||||
* @param totalSeconds 总秒数(如15)
|
||||
* @param onTick 每秒回调剩余秒数
|
||||
* @param onFinish 结束回调
|
||||
*/
|
||||
fun startCountDown(
|
||||
totalSeconds: Int = 15,
|
||||
onTick: (Int) -> Unit = {},
|
||||
onFinish: () -> Unit
|
||||
) {
|
||||
// 先取消旧倒计时
|
||||
cancelCountDown()
|
||||
|
||||
// 新建安全倒计时实例
|
||||
currentTimer = SafeCountDownTimer(
|
||||
millisInFuture = totalSeconds * 1000L,
|
||||
countDownInterval = 1000L,
|
||||
onTick = { millis ->
|
||||
// 转换为秒数回调
|
||||
val sec = (millis / 1000).toInt() + 1
|
||||
onTick.invoke(sec)
|
||||
},
|
||||
onFinish = {
|
||||
onFinish.invoke()
|
||||
}
|
||||
)
|
||||
|
||||
// 启动倒计时
|
||||
currentTimer?.start()
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消当前倒计时
|
||||
*/
|
||||
fun cancelCountDown() {
|
||||
currentTimer?.cancel()
|
||||
currentTimer = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.sw.platecabinet.utils
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.ranges.downTo
|
||||
|
||||
/**
|
||||
* 人脸识别防抖倒计时工具类(单例+防重复+无死循环)
|
||||
*/
|
||||
object CountDownUtil {
|
||||
// 原子布尔值:标记是否正在倒计时(防重复启动)
|
||||
private val isCounting = AtomicBoolean(false)
|
||||
|
||||
// 当前倒计时Job(用于取消)
|
||||
private var currentJob: Job? = null
|
||||
|
||||
/**
|
||||
* 启动/重启防抖倒计时(同一时间仅一个倒计时运行)
|
||||
* @param totalSeconds 倒计时总秒数(如15)
|
||||
* @param lifecycleScope Activity/Fragment的lifecycleScope(必须)
|
||||
* @param onTick 每秒回调剩余秒数(主线程)
|
||||
* @param onFinish 倒计时结束回调(主线程)
|
||||
*/
|
||||
fun startCountDown(
|
||||
total: Int = 15,
|
||||
lifecycleScope: CoroutineScope,
|
||||
onStart: () -> Unit = {},
|
||||
onTick: (Int) -> Unit = {},
|
||||
onFinish: () -> Unit
|
||||
) {
|
||||
// 1. 防重复:如果正在倒计时,先取消旧任务
|
||||
if (isCounting.get()) {
|
||||
//cancelCountDown()
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 标记为正在倒计时
|
||||
isCounting.set(true)
|
||||
|
||||
// 3. 启动新倒计时
|
||||
currentJob = flow {
|
||||
Log.d("CountDownUtil", "倒计时启动,总时长:$total 秒")
|
||||
for (i in total downTo 1) {
|
||||
emit(i)
|
||||
delay(1000) // IO线程等待1秒,无死循环
|
||||
}
|
||||
}
|
||||
.flowOn(Dispatchers.IO) // 上游IO线程,避免阻塞主线程
|
||||
.onStart {
|
||||
withContext(Dispatchers.Main) {
|
||||
onStart.invoke()
|
||||
}
|
||||
}
|
||||
.onEach { remaining ->
|
||||
// 主线程回调剩余时间
|
||||
withContext(Dispatchers.Main) {
|
||||
onTick.invoke(remaining)
|
||||
}
|
||||
}
|
||||
.onCompletion {
|
||||
// 倒计时结束:重置状态+回调
|
||||
withContext(Dispatchers.Main) {
|
||||
isCounting.set(false) // 重置标记
|
||||
currentJob = null // 置空Job
|
||||
Log.d("CountDownUtil", "倒计时正常结束")
|
||||
onFinish.invoke()
|
||||
}
|
||||
}
|
||||
.catch { e ->
|
||||
// 捕获异常:避免崩溃,重置状态
|
||||
Log.e("CountDownUtil", "倒计时异常:${e.message}", e)
|
||||
isCounting.set(false)
|
||||
currentJob = null
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消倒计时(手动停止)
|
||||
*/
|
||||
fun cancelCountDown() {
|
||||
currentJob?.cancel() // 取消Job
|
||||
currentJob = null // 置空,避免残留
|
||||
isCounting.set(false) // 重置标记
|
||||
Log.d("CountDownUtil", "倒计时被手动取消")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user