You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio Kotlin调用相机拍照保存失败且相机冻结求助

问题分析与修复方案

核心问题点

  1. Preview未关联UI组件:代码创建了Preview实例,但未绑定到界面的PreviewView,导致相机预览无输出,引发界面冻结。
  2. 不可靠的延迟拍照逻辑:固定2秒延迟触发拍照无法保证相机完全就绪,此时调用takePicture极易触发"Processing failed"错误。
  3. 状态管理缺失:isCameraInitialized仅在绑定成功后设为true,但拍照完成/失败、相机解绑后未重置,导致后续无法再次调用函数。
  4. 路径合法性未校验:folderPath需符合MediaStore规范(如"Pictures/HandCaptures"),无效路径会导致保存失败。

修复步骤与代码调整

1. 绑定Preview到UI组件

在布局中添加PreviewView(示例id为previewView),代码中为Preview设置SurfaceProvider:

val previewView = (context as Activity).findViewById<PreviewView>(R.id.previewView)
preview.setSurfaceProvider(previewView.surfaceProvider)

2. 替换延迟拍照为即时触发

移除Handler.postDelayed,绑定相机后直接调用takePicture,无需固定延迟。

3. 完善状态重置逻辑

在拍照回调中解绑相机并重置isCameraInitialized,确保后续可再次调用函数:

override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
    Log.d("MediaStore", "Hand image saved successfully.")
    cameraProvider.unbindAll()
    isCameraInitialized = false
}

override fun onError(exception: ImageCaptureException) {
    Log.e("MediaStore", "Failed to save hand image: ${exception.message}")
    cameraProvider.unbindAll()
    isCameraInitialized = false
}

4. 权限校验前置

调用函数前必须确保已获取CAMERA权限,Android 10以下还需WRITE_EXTERNAL_STORAGE权限:

if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
    Log.e("CameraX", "Camera permission missing")
    return
}

修复后的完整代码

private fun captureAndSaveImage(context: Context, folderPath: String) {
    // 权限校验
    if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        Log.e("CameraX", "Camera permission missing")
        return
    }
    if (isCameraInitialized) return

    val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
    cameraProviderFuture.addListener({
        val cameraProvider = cameraProviderFuture.get()
        val preview = Preview.Builder().build()
        val imageCapture = ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) // 优化拍照延迟
            .build()

        val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

        try {
            cameraProvider.unbindAll()
            // 绑定Preview到界面组件
            val previewView = (context as Activity).findViewById<PreviewView>(R.id.previewView)
            preview.setSurfaceProvider(previewView.surfaceProvider)

            cameraProvider.bindToLifecycle(
                context as androidx.lifecycle.LifecycleOwner,
                cameraSelector,
                preview,
                imageCapture
            )
            isCameraInitialized = true

            val photoFile = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, "Detected_Hand_Image_${System.currentTimeMillis()}.jpg") // 避免重名覆盖
                put(MediaStore.MediaColumns.RELATIVE_PATH, folderPath)
                put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            }

            val resolver = context.contentResolver
            val outputOptions = ImageCapture.OutputFileOptions.Builder(
                resolver,
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                photoFile
            ).build()

            // 直接触发拍照
            imageCapture.takePicture(
                outputOptions,
                ContextCompat.getMainExecutor(context),
                object : ImageCapture.OnImageSavedCallback {
                    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                        Log.d("MediaStore", "Hand image saved successfully.")
                        cameraProvider.unbindAll()
                        isCameraInitialized = false
                    }

                    override fun onError(exception: ImageCaptureException) {
                        Log.e("MediaStore", "Failed to save hand image: ${exception.message}")
                        cameraProvider.unbindAll()
                        isCameraInitialized = false
                    }
                }
            )

        } catch (exc: Exception) {
            Log.e("CameraX", "Failed to bind camera use cases", exc)
            isCameraInitialized = false
        }
    }, ContextCompat.getMainExecutor(context))
}

额外注意事项

  • 布局中必须包含PreviewView,示例布局代码:
    <androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
  • Android 13及以上需申请READ_MEDIA_IMAGES权限以访问保存的图片;Android 10-12无需额外存储权限(通过MediaStore写入)。

内容的提问来源于stack exchange,提问作者Ayman Saeid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:18:18