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

Kotlin应用首次调用未启动过的系统相机时无拍照返回结果

相机调用异常问题描述

我在应用开发中遇到一个可在模拟器、真机稳定复现的相机调用异常,场景为通过DialogFragment调起相机拍照后上传至远程服务,实现代码如下:

private val cameraPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted ->
    if (isGranted) {
        makeImageWithCamera()
    } else {
        dismiss()
    }
}

private val cameraLauncher = registerForActivityResult(
    ActivityResultContracts.TakePicture()
) { isSuccess ->
    if (isSuccess) {
        path?.let { path ->
            viewModel.setPhoto(path = path)
        }
    }
    dismiss()
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    subscribeFlow(viewModel.action) { dismiss() }
    with(binding) {
        buttonCamera.setOnClickListener { useCamera() }
        buttonCancel.setOnClickListener { dismiss() }
    }
}

private fun useCamera() {
     val isGranted = ContextCompat.checkSelfPermission(requireContext(), PERM_CAMERA) ==
                PackageManager.PERMISSION_GRANTED
     if (!isGranted) {
         cameraPermissionLauncher.launch(PERM_CAMERA)
     } else {
         makeImageWithCamera()
     }
}

private fun makeImageWithCamera() {
    try {
        val photoFileName = "photo${id}"
        val photosDir = requireContext()
            .getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        val photoFile = File.createTempFile(photoFileName, ".jpg", photosDir)
        val photoUri: Uri = FileProvider.getUriForFile(
            requireContext(),
            "app.provider",
            photoFile
        )
        path = photoFile.absolutePath
        cameraLauncher.launch(photoUri)
    } catch (err : IOException) {
        Log.e(TAG, err.stackTraceToString())
    }
}

复现规则

问题仅在设备从未启动过系统相机应用的场景下触发,复现步骤:

  • 应用内点击拍照按钮
  • 弹出应用自身的相机权限申请弹窗,点击允许
  • 系统相机启动,弹出相机自身的地理位置权限申请(用于照片位置标签),无论选择允许/拒绝都会复现问题
  • 进入相机拍摄界面后点击拍摄可正常拍照,但拍照后不会进入确认返回流程,停留在拍摄界面支持无限次拍摄,表现和手动单独启动系统相机完全一致
  • 按返回键回到应用,无照片返回回调触发,对话框直接关闭

应用自身的权限申请弹窗
系统相机的权限申请弹窗
系统相机首次启动界面
相机表现与独立启动时一致
问题状态:支持无限次拍照无返回

正常场景表现

第二次重复调用拍照流程,或提前手动启动过一次系统相机、处理完相机自身的权限申请后,问题消失:相机拍摄单张照片后弹出确认按钮,点击确认正常返回应用,回调返回拍摄照片供后续处理。
正常状态:拍照后可确认返回应用

问题影响

异常不仅影响用户体验,还极高概率导致Firebase自动化机器人测试在该流程失败。

根本原因

问题核心是系统相机首次冷启动时的Intent参数透传失败:

  • 你通过ActivityResultContracts.TakePicture发起的隐式Intent会携带EXTRA_OUTPUT参数,同时标记需要相机在拍摄完成后返回结果给调用方,相机收到该参数后才会进入单拍确认返回的模式。
  • 当设备从未启动过系统相机时,调起相机后不会直接进入拍照界面,会先在相机自身的任务栈中依次展示首次启动引导页、自身所需的权限申请页。这些独立的Activity跳转过程中,绝大多数系统相机的实现不会把原始Intent中的EXTRA_OUTPUT、结果返回标记透传到最终的拍照Activity,导致拍照Activity判定自身为用户从桌面手动启动,进入普通拍照模式,支持无限次拍摄、拍完不会自动返回调用方。
  • 手动启动一次相机走完初始化流程后,后续调起相机不会再展示引导、权限申请页,直接进入拍照Activity,能正常读取传入的Intent参数,流程恢复正常。

修复方案

  • 兼容性最高的方案:不依赖相机的Activity结果回调,启动相机前记录待保存的照片路径,在DialogFragment/宿主Activity的onResume生命周期中主动检查对应路径的照片文件是否生成、更新时间是否匹配,符合条件就直接走后续上传逻辑,可覆盖所有厂商相机的兼容问题。
  • 轻量优化方案:自定义拍照ActivityResult合约,给启动相机的Intent添加Intent.FLAG_ACTIVITY_SINGLE_TOP、Intent.FLAG_ACTIVITY_CLEAR_TOP标记,减少相机跨页面跳转时的参数丢失概率。
  • 预初始化方案:应用首次安装启动时,隐式调起一次系统相机主界面(不需要触发拍照流程),让相机提前完成首次初始化和权限申请,不过该方案会在用户首次打开应用时突兀弹出相机,体验较差,不推荐使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 00:24:24