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

Android存储权限异常:调用图库无权限弹窗,相机权限正常

问题概述

开发的简易WebSocket应用支持发送消息与附件,后续计划拓展更多功能。目前调用设备图库选择图片时,日志显示已触发图库启动,但权限请求弹窗无法弹出;相机权限的请求弹窗则能正常显示。同时需要排查代码中的冗余部分。

控制台日志

2023-08-07 08:55:56.818  3492-3492  MainFragment            com.example.websoc                   D  Launching gallery intent...

原始代码

companion object {
        fun newInstance() = MainFragment()
        private const val REQUEST_IMAGE_CAPTURE = 1

        private const val PERMISSION_CODE = 1001
    }

 private lateinit var currentPhotoPath: String
    private var selectedImageUri: Uri? = null
private fun setupPermissions() {
        val permission = ContextCompat.checkSelfPermission(
            requireContext(),
            Manifest.permission.READ_MEDIA_IMAGES
        )

        if (permission != PackageManager.PERMISSION_GRANTED) {
            Log.i(TAG, "Permission denied")
        }
    }

// this is in onViewCreated
   val galleryButton = view.findViewById<ImageButton>(R.id.galleryButton)
        galleryButton.setOnClickListener {
            openGallery()
            requestGalleryPermission()

        }

  private val galleryActivityResultLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                Log.d(TAG, "Gallery intent result: success")
                val intent = result.data
                intent?.data?.let { uri ->
                    Log.d(TAG, "Selected image URI: $uri")
                    // Handle the selected image URI here
                }
            } else {
                Log.d(TAG, "Gallery intent was canceled or failed.")
            }
        }



private fun requestGalleryPermission() {
        if (ContextCompat.checkSelfPermission(
                requireContext(),
                Manifest.permission.READ_EXTERNAL_STORAGE
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            Log.d(TAG, "Gallery permission granted. Launching gallery intent...")
            val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
            galleryActivityResultLauncher.launch(intent)
        } else {
            ActivityCompat.requestPermissions(
                requireActivity(),
                arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
                PERMISSION_CODE
            )
        }
    }
 override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            PERMISSION_CODE -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Gallery permission granted. Launching gallery intent...")
                    val intent =
                        Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
                    galleryActivityResultLauncher.launch(intent)
                } else {
                    Log.d(TAG, "Gallery permission denied.")
                }
            }
        }
    }


    private fun openMenu() {
        val galleryButton = view?.findViewById<ImageButton>(R.id.galleryButton)

        val popupMenu = PopupMenu(requireContext(), galleryButton)

        // Inflate the menu resource
        popupMenu.menuInflater.inflate(R.menu.gallary_menu, popupMenu.menu)
        // Set a listener for menu item click events
        popupMenu.setOnMenuItemClickListener { menuItem ->
            when (menuItem.itemId) {
                R.id.menu_gallery -> {
                    // Handle the gallery menu item click here
                    Log.d("MainFragment", "Launching gallery intent...")
                    requestGalleryPermission()
                    true
                }

                R.id.menu_cam -> {
                    // Handle the "Open Camera" menu item click here
                    openCamera()
                    true
                }

                else -> false
            }
        }
        popupMenu.show()
    }

问题分析与优化方案

1. 权限请求不弹窗的核心原因

  • 权限API版本不兼容:代码中同时涉及READ_MEDIA_IMAGES(Android 13+)和READ_EXTERNAL_STORAGE(Android 12及以下),但实际请求权限时只申请了READ_EXTERNAL_STORAGE。在Android 13及以上设备中,READ_EXTERNAL_STORAGE已被废弃,系统会忽略该权限的请求,导致弹窗不出现。
  • 冗余的权限检查方法:setupPermissions方法仅打印权限状态日志,未执行任何实际权限请求逻辑,属于无效代码。

2. 代码冗余点排查

  • 重复的图库启动逻辑:requestGalleryPermission方法中两次编写了启动图库的Intent代码,可提取为单独方法复用。
  • 点击事件逻辑冲突:galleryButton的点击事件同时调用了未实现的openGallery方法和requestGalleryPermission,而openMenu中的菜单点击也调用requestGalleryPermission,逻辑重复且混乱。
  • 旧版权限回调混用:使用了onRequestPermissionsResult(旧版回调)和ActivityResultLauncher(新版API),两者混用易导致逻辑混乱。

3. 优化后的代码

companion object {
    fun newInstance() = MainFragment()
    private const val REQUEST_IMAGE_CAPTURE = 1
}

private lateinit var currentPhotoPath: String
private var selectedImageUri: Uri? = null

// 新版权限请求Launcher,适配Android全版本
private val galleryPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
    if (isGranted) {
        Log.d(TAG, "Gallery permission granted. Launching gallery intent...")
        launchGalleryIntent()
    } else {
        Log.d(TAG, "Gallery permission denied.")
    }
}

private val galleryActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        Log.d(TAG, "Gallery intent result: success")
        result.data?.data?.let { uri ->
            Log.d(TAG, "Selected image URI: $uri")
            selectedImageUri = uri
            // 处理选中的图片URI
        }
    } else {
        Log.d(TAG, "Gallery intent was canceled or failed.")
    }
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    val galleryButton = view.findViewById<ImageButton>(R.id.galleryButton)
    galleryButton.setOnClickListener {
        openMenu()
    }
}

private fun requestGalleryPermission() {
    val targetPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        Manifest.permission.READ_MEDIA_IMAGES
    } else {
        Manifest.permission.READ_EXTERNAL_STORAGE
    }

    when {
        ContextCompat.checkSelfPermission(requireContext(), targetPermission) == PackageManager.PERMISSION_GRANTED -> {
            launchGalleryIntent()
        }
        shouldShowRequestPermissionRationale(targetPermission) -> {
            // 可在此显示权限说明弹窗
            Log.d(TAG, "Need to explain permission usage")
        }
        else -> {
            galleryPermissionLauncher.launch(targetPermission)
        }
    }
}

// 提取图库启动逻辑,避免重复代码
private fun launchGalleryIntent() {
    val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
    galleryActivityResultLauncher.launch(intent)
}

private fun openMenu() {
    val galleryButton = view?.findViewById<ImageButton>(R.id.galleryButton) ?: return
    
    val popupMenu = PopupMenu(requireContext(), galleryButton)
    popupMenu.menuInflater.inflate(R.menu.gallary_menu, popupMenu.menu)
    
    popupMenu.setOnMenuItemClickListener { menuItem ->
        when (menuItem.itemId) {
            R.id.menu_gallery -> {
                Log.d("MainFragment", "Launching gallery intent...")
                requestGalleryPermission()
                true
            }
            R.id.menu_cam -> {
                openCamera()
                true
            }
            else -> false
        }
    }
    popupMenu.show()
}

// 移除冗余的setupPermissions方法
// 移除旧版onRequestPermissionsResult回调

优化说明

  • 统一权限处理逻辑,根据Android版本自动选择对应的权限申请字段,解决Android 13+权限请求不弹窗的问题。
  • 提取launchGalleryIntent方法,消除重复的图库启动代码。
  • 简化galleryButton的点击事件,直接调用openMenu,避免逻辑冲突。
  • 使用新版ActivityResultContracts.RequestPermission替代旧版onRequestPermissionsResult,逻辑更清晰。
  • 移除无效的setupPermissions方法,精简代码结构。

内容的提问来源于stack exchange,提问作者MãĴď

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:57:30