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

API 33以下版本相机无法工作问题排查求助

API33以下设备Dexter权限校验失效问题排查与修复

问题描述

实现带音频录制的相机功能,使用Dexter做权限校验,视频可保存到设备文件夹。该功能在API32及以上设备正常运行,但API33以下设备无法启动,调试发现Dexter的权限校验逻辑未正常执行。

原始代码片段

AndroidManifest.xml

<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:required="false" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

Fragment.kt(关键代码)

private fun initCamera() {
    cameraExecutor = Executors.newSingleThreadExecutor()
    requestCameraPermission()
}

private fun requestCameraPermission() {
    Dexter.withContext(requireContext())
        .withPermissions(
            Manifest.permission.RECORD_AUDIO,
            Manifest.permission.CAMERA
        ).withListener(object : MultiplePermissionsListener {
            override fun onPermissionsChecked(p0: MultiplePermissionsReport?) {
                p0?.let {
                    if (it.areAllPermissionsGranted()) {
                        startCamera(CameraSelector.DEFAULT_BACK_CAMERA)
                    }
                }
            }

            override fun onPermissionRationaleShouldBeShown(
                p0: MutableList<PermissionRequest>?,
                p1: PermissionToken?
            ) {
                p1?.continuePermissionRequest()
            }
        }).check()
}

private fun startCamera(cameraSelector: CameraSelector) {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
    val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

    cameraProviderFuture.addListener({
        // Preview配置
        val preview = Preview.Builder()
            .build()
            .also {
                binding.previewViewTalkerDuelCreateNewDuelTakeVideo.implementationMode =
                    PreviewView.ImplementationMode.COMPATIBLE
                it.setSurfaceProvider(
                    binding.previewViewTalkerDuelCreateNewDuelTakeVideo.surfaceProvider
                )
            }

        val recorder = Recorder.Builder()
            .setQualitySelector(
                QualitySelector.from(
                    Quality.HIGHEST,
                    FallbackStrategy.higherQualityOrLowerThan(Quality.SD)
                )
            )
            .build()

        videoCapture = VideoCapture.withOutput(recorder)

        try {
            cameraProvider.unbindAll()
            cameraProvider.bindToLifecycle(
                this, cameraSelector, preview, videoCapture
            )
        } catch (exc: Exception) {
            exc.printStackTrace()
        }
    }, ContextCompat.getMainExecutor(requireContext()))
}

Dexter版本

implementation 'com.karumi:dexter:6.2.3'

核心问题分析

  1. 主线程阻塞导致Dexter权限流程中断
    startCamera方法中直接调用cameraProviderFuture.get()同步获取ProcessCameraProvider,会阻塞主线程(startCamera由权限回调触发,运行在主线程),导致主线程卡死,Dexter无法弹出权限弹窗、触发回调,表现为权限校验逻辑“未正常执行”。

  2. AndroidManifest配置错误
    将MANAGE_EXTERNAL_STORAGE权限错误声明为<uses-feature>,该权限属于特殊权限,正确声明应为<uses-permission>,且Dexter不支持自动处理此类权限。

  3. 存储权限覆盖不全(视频保存环节)
    API29-32(Android10-12)设备使用Scoped Storage,若需保存视频到公共目录,当前Manifest的权限配置不足,但这是视频保存问题,并非相机无法启动的直接原因。

修复步骤

1. 修复startCamera的线程阻塞问题

移除同步get()调用,将cameraProvider的获取放到addListener回调中,避免阻塞主线程:

private fun startCamera(cameraSelector: CameraSelector) {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())

    cameraProviderFuture.addListener({
        // 在回调中安全获取cameraProvider,此时Future已完成
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

        // Preview配置
        val preview = Preview.Builder()
            .build()
            .also {
                binding.previewViewTalkerDuelCreateNewDuelTakeVideo.implementationMode =
                    PreviewView.ImplementationMode.COMPATIBLE
                it.setSurfaceProvider(
                    binding.previewViewTalkerDuelCreateNewDuelTakeVideo.surfaceProvider
                )
            }

        // Recorder配置
        val recorder = Recorder.Builder()
            .setQualitySelector(
                QualitySelector.from(
                    Quality.HIGHEST,
                    FallbackStrategy.higherQualityOrLowerThan(Quality.SD)
                )
            )
            .build()

        videoCapture = VideoCapture.withOutput(recorder)

        try {
            // 解绑旧用例再绑定新用例
            cameraProvider.unbindAll()
            cameraProvider.bindToLifecycle(
                this, cameraSelector, preview, videoCapture
            )
        } catch (exc: Exception) {
            exc.printStackTrace()
        }
    }, ContextCompat.getMainExecutor(requireContext()))
}

2. 修正AndroidManifest的权限声明错误

将错误的<uses-feature>改为<uses-permission>,并添加版本限制:

<!-- 替换错误的uses-feature声明 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />

3. 确保Fragment生命周期正确调用initCamera

在onViewCreated中调用initCamera(),确保Fragment已关联有效Context,Dexter能正常初始化权限请求:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    initCamera()
}

4. 补充API29-32的存储权限处理(可选,针对视频保存)

若需在API29-32设备保存视频到公共目录,需引导用户手动开启MANAGE_EXTERNAL_STORAGE权限:

private fun checkManageStoragePermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
        val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
        intent.data = Uri.parse("package:${requireContext().packageName}")
        startActivity(intent)
    }
}

内容的提问来源于stack exchange,提问作者Ahmet Yılmaz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 04:27:03