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

DialogFragment中registerForActivityResult申请权限回调异常

问题说明

实现退出页面前提示用户保存数据的功能时,采用registerForActivityResult申请存储权限,出现以下异常:

  • 首次触发流程、权限未授予时,系统权限弹窗正常弹出,但用户点击同意授权后,权限回调完全不执行
  • 再次触发退出流程时,权限已经处于授予状态,不需要弹出系统权限弹窗,回调反而能正常返回授权成功结果

原实现代码如下:
SaveCalibrationDialogFragment 代码:

class SaveCalibrationDialogFragment(val data: String) : DialogFragment() {
    private val activityResultLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()) { isGranted ->
        println("Received CallBack")
        if (isGranted) {
            println("Permission granted!")
        } else {
            println("Permission needed!")
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(this.requireContext())
        val dialog = builder.setTitle("Notice")
            .setMessage("Data is not saved. Do you want to save before exiting?")
            .setNegativeButton("No") { dialog, _ ->
            }.setPositiveButton("Yes") { _, _ ->
                activityResultLauncher?.launch(WRITE_EXTERNAL_STORAGE)
            }
            .create()
        return dialog
    }

}

宿主Activity代码:

cancel.setOnClickListener {
        if (viewModel.isDataSave && viewModel.isLastPage) {
            setResult(Activity.RESULT_OK, intent)
            finish()
        } else if (!viewModel.isDataSave && viewModel.isLastPage){
            SaveCalibrationDialogFragment(viewModel.dataToSave!!).show(this.supportFragmentManager, "costumeSaveDialog")
        } else {
            setResult(Activity.RESULT_CANCELED, intent)
            finish()
        }

    }
故障原因

问题出在AlertDialog的默认按钮行为和registerForActivityResult的生命周期要求冲突:

  • AlertDialog的PositiveButton默认点击逻辑会直接dismiss当前对话框,外层DialogFragment会同步进入销毁流程,和Fragment绑定的ActivityResultLauncher随之退出活跃状态
  • 当用户在系统权限弹窗点击同意后,系统找不到处于活跃状态的launcher来分发权限结果,自然不会触发预设的回调逻辑
  • 第二次触发流程时,权限已经授予,系统不会弹出权限弹窗,会直接同步返回授权结果,此时新创建的DialogFragment刚完成初始化,launcher处于活跃状态,所以能正常收到回调
修复方案
  1. 覆写AlertDialog按钮的默认点击逻辑,不要在点击确认按钮时立刻关闭对话框,等权限结果返回后再手动控制对话框和页面的关闭逻辑
  2. 发起权限申请前先做权限状态检查,已经授权的场景直接走写入逻辑,不需要重复发起申请
  3. 不要在Activity弹出Dialog后立刻执行finish操作,所有页面关闭逻辑挪到DialogFragment的权限回调/按钮点击逻辑中处理,避免宿主提前销毁导致launcher失效

修复后的DialogFragment代码:

class SaveCalibrationDialogFragment(val data: String) : DialogFragment() {
    private val activityResultLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            // 执行文件写入逻辑
            // writeDataToFile(data)
            dismiss()
            requireActivity().setResult(Activity.RESULT_OK)
            requireActivity().finish()
        } else {
            Toast.makeText(requireContext(), "存储权限被拒绝,无法保存数据", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val builder = AlertDialog.Builder(requireContext())
        val dialog = builder.setTitle("Notice")
            .setMessage("Data is not saved. Do you want to save before exiting?")
            .setNegativeButton("No") { _, _ ->
                dismiss()
                requireActivity().setResult(Activity.RESULT_CANCELED)
                requireActivity().finish()
            }
            // 先传空监听,绕过默认点击即dismiss的逻辑
            .setPositiveButton("Yes", null)
            .create()

        // 对话框显示后再手动绑定确认按钮点击事件
        dialog.setOnShowListener {
            dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
                // 先检查权限状态,已授权直接写入
                if (ContextCompat.checkSelfPermission(
                        requireContext(),
                        Manifest.permission.WRITE_EXTERNAL_STORAGE
                    ) == PackageManager.PERMISSION_GRANTED
                ) {
                    // writeDataToFile(data)
                    dismiss()
                    requireActivity().setResult(Activity.RESULT_OK)
                    requireActivity().finish()
                    return@setOnClickListener
                }
                // 未授权才发起权限申请
                activityResultLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            }
        }
        return dialog
    }
}

补充适配提示:Android 13(API 33)及以上版本已废弃WRITE_EXTERNAL_STORAGE权限,写入应用私有目录不需要申请任何存储权限,写入公共媒体目录需要申请对应细分的媒体读取权限,无需申请写权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:48:43