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

Android Kotlin相机权限问题及弹窗BUG技术求助

问题描述

我正在开发一款基于Android Kotlin的相机目标检测应用,目前遇到以下问题:

  • 授予相机权限后应用显示黑屏,需重启1-2次才能成功打开相机;
  • 自定义权限提示弹窗存在两个BUG:
    • 用户点击弹窗“OK”后仍拒绝相机权限,应用会进入弹窗反复弹出的循环;
    • 初始拒绝权限后,通过弹窗“OK”授予权限时应用意外关闭。
相关代码

onCreate函数

if (ContextCompat.checkSelfPermission(
               this,
               android.Manifest.permission.CAMERA
           ) != PackageManager.PERMISSION_GRANTED
       )
           requestPermissions(arrayOf(android.Manifest.permission.CAMERA), 101)
       else
           detectObject()

权限处理函数

fun getPermission() {
    if (ContextCompat.checkSelfPermission(
            this,
            android.Manifest.permission.CAMERA
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        requestPermissions(arrayOf(android.Manifest.permission.CAMERA), 101)
    } else {
        detectObject()
    }
}

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == 101 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        detectObject()
    } else {
        val alert = PermissionDialog()
        alert.show(supportFragmentManager, null)
    }
}

权限弹窗Dialog类

import android.app.AlertDialog
import android.app.Dialog
import android.media.MediaPlayer
import android.os.Bundle
import android.widget.Toast
import androidx.fragment.app.DialogFragment
import com.example.sightfulkotlin.MainActivity
import com.example.sightfulkotlin.R

class PermissionDialog : DialogFragment() {

    private lateinit var mediaPlayer: MediaPlayer

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(activity)
        builder
            .setTitle("Camera Permission")
            .setIcon(android.R.drawable.ic_menu_camera)
            .setMessage("Camera permission denied")
            .setPositiveButton("Ok") { _, _ -> (activity as MainActivity).getPermission()}
            .setNegativeButton("Cancel") { _, _ ->
                Toast.makeText(context, "Sorry, we can't make the app work without the camera permission.", Toast.LENGTH_LONG).show()
            }
            .setCancelable(false)
        return builder.create()
    }
}
解决方案

1. 黑屏问题修复

黑屏多因权限授予后相机预览未正确初始化或重启,需确保detectObject()方法处理好相机资源的释放与重建:

  • 在detectObject()开头添加资源释放逻辑,避免旧资源占用导致新预览无法启动:
private fun detectObject() {
    // 先释放已绑定的相机资源
    cameraProvider?.unbindAll()
    // 重新初始化相机预览与目标检测逻辑
    // ... 原有业务代码
}
  • 确保onRequestPermissionsResult中调用detectObject()时在主线程执行,避免UI阻塞导致预览启动失败。

2. 权限弹窗BUG修复

(1)弹窗循环问题

当用户拒绝权限并勾选「不再询问」时,直接调用requestPermissions()会触发权限回调并重复弹出弹窗,需先判断是否需要显示解释弹窗:
修改onRequestPermissionsResult逻辑:

override fun onRequestPermissionsResult(
    requestCode: Int,
    permissions: Array<out String>,
    grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == 101) {
        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            detectObject()
        } else {
            // 判断用户是否勾选了「不再询问」
            val shouldShowRationale = shouldShowRequestPermissionRationale(android.Manifest.permission.CAMERA)
            if (shouldShowRationale) {
                // 用户仅拒绝权限,未勾选不再询问,显示弹窗
                val alert = PermissionDialog()
                alert.show(supportFragmentManager, null)
            } else {
                // 用户勾选不再询问,引导至应用设置页开启权限
                Toast.makeText(this, "请在应用设置中开启相机权限", Toast.LENGTH_LONG).show()
            }
        }
    }
}

(2)应用意外关闭问题

崩溃原因多为(activity as MainActivity)强转失败(Activity已销毁时),修改弹窗点击事件的安全转换逻辑:

// 在PermissionDialog中替换PositiveButton的点击处理
.setPositiveButton("Ok") { _, _ ->
    context?.let { ctx ->
        (ctx as? MainActivity)?.getPermission() ?: run {
            Toast.makeText(ctx, "无法获取权限,请重启应用", Toast.LENGTH_SHORT).show()
        }
    }
}

同时在MainActivity销毁时清理弹窗,避免内存泄漏:

override fun onDestroy() {
    supportFragmentManager.findFragmentByTag(null)?.let { fragment ->
        (fragment as? DialogFragment)?.dismiss()
    }
    super.onDestroy()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 19:05:39