From b6ed47ff876e6347ca09797c138f15e13d5bec65 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Tue, 30 Jun 2026 15:54:45 +0800 Subject: [PATCH] =?UTF-8?q?fix(camera):=20=E8=A7=A3=E5=86=B3=E7=9B=B8?= =?UTF-8?q?=E6=9C=BA=E6=8B=8D=E7=85=A7=E5=8A=9F=E8=83=BD=E7=9A=84=E7=AB=9E?= =?UTF-8?q?=E6=80=81=E6=9D=A1=E4=BB=B6=E5=92=8C=E9=87=8D=E8=AF=95=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复了相机回调中的竞态条件,使用isShowCamera标识查找空闲槽位 - 将图片处理逻辑移至IO线程,避免主线程阻塞 - 添加了照片拍摄失败回调处理函数 - 简化了相机拍照调用方式,直接传递回调函数 - 在FoodCollectionAdapter中使用Glide加载图片uri - 添加了相机绑定失败时的自动重试机制,包含延迟重试逻辑 - 增加了详细的错误日志记录和异常处理 --- .../activity/fragment/CollectFragment.kt | 37 +++++++++---------- .../adapter/FoodCollectionAdapter.kt | 2 +- .../sw/dualscreen/utils/PhotoCaptureHelper.kt | 20 ++++++---- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt b/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt index 35e8957..8d4f162 100644 --- a/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt +++ b/app/src/main/java/com/sw/dualscreen/activity/fragment/CollectFragment.kt @@ -98,15 +98,16 @@ class CollectFragment : BaseFragment() { } - private val cameraCallback: (Uri) -> Unit = cameraCallback@{ uri -> + private val cameraCallback: (Uri) -> Unit = { uri -> try { - val index = foodCollectionList.indexOfFirst { it.imageFile == null } - if (index == -1) { - ToastUtils.showToast("每次只允许保存${MAX_COUNT}条数据") - settingActivity?.hideWaitingDialog() - return@cameraCallback - } activity?.lifecycleScope?.launch(Dispatchers.Main) { + // 主线程原子操作:查找空闲槽位并立即标记,避免竞态 + val index = foodCollectionList.indexOfFirst { it.isShowCamera } + if (index == -1) { + ToastUtils.showToast("每次只允许保存${MAX_COUNT}条数据") + settingActivity?.hideWaitingDialog() + return@launch + } foodCollectionList[index].let { it.imageVector = null it.bitmap = null @@ -115,11 +116,11 @@ class CollectFragment : BaseFragment() { it.imageUri = uri } collectionAdapter.notifyItemChanged(index) - } -// settingActivity?.hideWaitingDialog() - activity?.lifecycleScope?.launch(Dispatchers.IO) { - ImageUtil.uriToBitmap(requireActivity(), uri)?.let { bitmap -> - getImageVector(index, bitmap) + // IO线程处理bitmap + launch(Dispatchers.IO) { + ImageUtil.uriToBitmap(requireActivity(), uri)?.let { bitmap -> + getImageVector(index, bitmap) + } } } } catch (e: Exception) { @@ -130,6 +131,10 @@ class CollectFragment : BaseFragment() { } } + 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() { } 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 diff --git a/app/src/main/java/com/sw/dualscreen/adapter/FoodCollectionAdapter.kt b/app/src/main/java/com/sw/dualscreen/adapter/FoodCollectionAdapter.kt index 3bb0d13..72702ba 100644 --- a/app/src/main/java/com/sw/dualscreen/adapter/FoodCollectionAdapter.kt +++ b/app/src/main/java/com/sw/dualscreen/adapter/FoodCollectionAdapter.kt @@ -40,7 +40,7 @@ class FoodCollectionAdapter (var list: MutableList) : //setImageURI(it.imageUri) //setImageBitmap(it.bitmap) if (it.imageUri!=null) { - setImageURI(it.imageUri) + load(it.imageUri) } else { load(it.imageFile) } diff --git a/app/src/main/java/com/sw/dualscreen/utils/PhotoCaptureHelper.kt b/app/src/main/java/com/sw/dualscreen/utils/PhotoCaptureHelper.kt index f58d1f0..95d13f8 100644 --- a/app/src/main/java/com/sw/dualscreen/utils/PhotoCaptureHelper.kt +++ b/app/src/main/java/com/sw/dualscreen/utils/PhotoCaptureHelper.kt @@ -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) { - bindCamera?.invoke() - //takePhoto() - } - + if (errorMsg.contains("Not bound to a valid Camera") && !isRetry) { + Timber.w(exception, "相机未绑定,尝试重新绑定后重试") + bindCamera?.invoke() + // 延迟重试,等待相机异步绑定完成 + Handler(Looper.getMainLooper()).postDelayed({ + takePhoto(fileNamePrefix, fileExtension, isRetry = true) + }, 300) + return } Timber.e(exception, errorMsg) onError(errorMsg)