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

使用CameraX开发安卓相机APP时EXIF数据更新失败无法拍照

问题分析

从错误栈最底层的java.io.IOException: Not a directory可定位核心问题:

  1. 提前创建空文件干扰EXIF写入:你在调用imageCapture.takePicture()前手动执行了photoFile.createNewFile(),CameraX的图片捕获流程会自行处理文件创建,提前生成的空文件会导致EXIF工具类在生成临时文件时出现路径识别错误,进而引发EXIF更新失败。
  2. 权限请求不完整:权限检查时同时验证了CAMERA和WRITE_EXTERNAL_STORAGE,但实际请求权限时只发起了CAMERA权限申请,若WRITE_EXTERNAL_STORAGE权限缺失,会导致文件写入操作失败。
解决方案

1. 修正拍照按钮的onClick函数

移除手动创建文件的代码,让CameraX自行处理文件创建:

private fun onClick(imageCapture: ImageCapture) {
    // 创建保存目录
    val photoDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    if (photoDir == null || !photoDir.isDirectory) {
        Log.d("MainActivity", "Invalid photo directory: $photoDir")
        return
    }
    Log.d("MainActivity", "photoDir: $photoDir")

    // 生成唯一文件名
    val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
    val photoFile = File(photoDir, "${timeStamp}.jpg")

    Log.d("MainActivity", "photoFilePath: ${photoFile.absolutePath}")

    // 配置输出选项
    val outputFileOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()

    // 执行拍照
    imageCapture.takePicture(outputFileOptions, ContextCompat.getMainExecutor(this),
        object : ImageCapture.OnImageSavedCallback {
            override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                runOnUiThread {
                    Toast.makeText(this@MainActivity,"Capturing succeeded",Toast.LENGTH_SHORT).show()
                }
            }
            override fun onError(error: ImageCaptureException) {
                runOnUiThread {
                    Log.d("MainActivity", "Error capturing image: ${error.message}", error)
                    Toast.makeText(this@MainActivity,"Capturing failed",Toast.LENGTH_SHORT).show()
                }
            }
        })
}

2. 修复权限请求逻辑

确保根据系统版本正确请求所需权限(Android 11+无需WRITE_EXTERNAL_STORAGE即可写入应用私有目录):

private fun requestCameraPermissionIfMissing(onResult: ((Boolean) -> Unit)) {
    val requiredPermissions = mutableListOf<String>()
    if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        requiredPermissions.add(android.Manifest.permission.CAMERA)
    }
    // Android 10及以下需要WRITE权限
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && 
        ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        requiredPermissions.add(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
    }

    if (requiredPermissions.isEmpty()) {
        onResult(true)
    } else {
        registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
            val allGranted = permissions.values.all { it }
            onResult(allGranted)
        }.launch(requiredPermissions.toTypedArray())
    }
}

3. 额外优化建议

  • 添加cameraExecutor的销毁逻辑,防止内存泄漏:
override fun onDestroy() {
    super.onDestroy()
    cameraExecutor.shutdown()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 03:17:03