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ãĴď
相关产品推荐
相关产品推荐

