fix(camera): 解决相机拍照功能的竞态条件和重试机制问题
- 修复了相机回调中的竞态条件,使用isShowCamera标识查找空闲槽位 - 将图片处理逻辑移至IO线程,避免主线程阻塞 - 添加了照片拍摄失败回调处理函数 - 简化了相机拍照调用方式,直接传递回调函数 - 在FoodCollectionAdapter中使用Glide加载图片uri - 添加了相机绑定失败时的自动重试机制,包含延迟重试逻辑 - 增加了详细的错误日志记录和异常处理
This commit is contained in:
@@ -98,15 +98,16 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
}
|
||||
|
||||
|
||||
private val cameraCallback: (Uri) -> Unit = cameraCallback@{ uri ->
|
||||
private val cameraCallback: (Uri) -> Unit = { uri ->
|
||||
try {
|
||||
val index = foodCollectionList.indexOfFirst { it.imageFile == null }
|
||||
activity?.lifecycleScope?.launch(Dispatchers.Main) {
|
||||
// 主线程原子操作:查找空闲槽位并立即标记,避免竞态
|
||||
val index = foodCollectionList.indexOfFirst { it.isShowCamera }
|
||||
if (index == -1) {
|
||||
ToastUtils.showToast("每次只允许保存${MAX_COUNT}条数据")
|
||||
settingActivity?.hideWaitingDialog()
|
||||
return@cameraCallback
|
||||
return@launch
|
||||
}
|
||||
activity?.lifecycleScope?.launch(Dispatchers.Main) {
|
||||
foodCollectionList[index].let {
|
||||
it.imageVector = null
|
||||
it.bitmap = null
|
||||
@@ -115,13 +116,13 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
it.imageUri = uri
|
||||
}
|
||||
collectionAdapter.notifyItemChanged(index)
|
||||
}
|
||||
// settingActivity?.hideWaitingDialog()
|
||||
activity?.lifecycleScope?.launch(Dispatchers.IO) {
|
||||
// IO线程处理bitmap
|
||||
launch(Dispatchers.IO) {
|
||||
ImageUtil.uriToBitmap(requireActivity(), uri)?.let { bitmap ->
|
||||
getImageVector(index, bitmap)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
settingActivity?.hideWaitingDialog()
|
||||
@@ -130,6 +131,10 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
private val photoFailCallback: (String) -> Unit = { errMsg ->
|
||||
settingActivity?.hideWaitingDialog()
|
||||
}
|
||||
|
||||
private fun getImageVector(index: Int, bitmap: Bitmap) {
|
||||
// val bitmap = BitmapCropper.cropCenter(
|
||||
// original = srcBmp,
|
||||
@@ -171,13 +176,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
}
|
||||
|
||||
settingActivity?.showWaitingDialog("采集中……")
|
||||
cameraUtils.takePhoto(succCallback = { uri ->
|
||||
settingActivity?.lifecycleScope?.launch((Dispatchers.IO)) {
|
||||
cameraCallback(uri)
|
||||
}
|
||||
}, failCallback = { errMsg ->
|
||||
settingActivity?.hideWaitingDialog()
|
||||
})
|
||||
cameraUtils.takePhoto(succCallback = cameraCallback, failCallback = photoFailCallback)
|
||||
}
|
||||
|
||||
private var cameraErrorCount = 0
|
||||
|
||||
@@ -40,7 +40,7 @@ class FoodCollectionAdapter (var list: MutableList<FoodCollectionBean>) :
|
||||
//setImageURI(it.imageUri)
|
||||
//setImageBitmap(it.bitmap)
|
||||
if (it.imageUri!=null) {
|
||||
setImageURI(it.imageUri)
|
||||
load(it.imageUri)
|
||||
} else {
|
||||
load(it.imageFile)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.sw.dualscreen.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.camera.core.ImageCapture
|
||||
import androidx.camera.core.ImageCaptureException
|
||||
@@ -42,7 +44,8 @@ class PhotoCaptureHelper(
|
||||
*/
|
||||
fun takePhoto(
|
||||
fileNamePrefix: String = "IMG_",
|
||||
fileExtension: String = ".jpg"
|
||||
fileExtension: String = ".jpg",
|
||||
isRetry: Boolean = false
|
||||
) {
|
||||
Timber.d("开始拍照采集")
|
||||
|
||||
@@ -70,13 +73,16 @@ class PhotoCaptureHelper(
|
||||
}
|
||||
|
||||
override fun onError(exception: ImageCaptureException) {
|
||||
Timber.e(exception)
|
||||
val errorMsg = "拍照失败: ${exception.message}"
|
||||
if (errorMsg.contains("Not bound to a valid Camera")) {
|
||||
if (bindCamera != null) {
|
||||
if (errorMsg.contains("Not bound to a valid Camera") && !isRetry) {
|
||||
Timber.w(exception, "相机未绑定,尝试重新绑定后重试")
|
||||
bindCamera?.invoke()
|
||||
//takePhoto()
|
||||
}
|
||||
|
||||
// 延迟重试,等待相机异步绑定完成
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
takePhoto(fileNamePrefix, fileExtension, isRetry = true)
|
||||
}, 300)
|
||||
return
|
||||
}
|
||||
Timber.e(exception, errorMsg)
|
||||
onError(errorMsg)
|
||||
|
||||
Reference in New Issue
Block a user