Android Studio相册权限自动拒绝致崩溃问题求助
安卓相册权限申请崩溃问题排查与修复
问题场景
开发Android项目时,在Fragment中通过按钮点击申请相册访问权限,出现权限被自动拒绝且应用崩溃的情况。已在Manifest文件中声明权限,并编写了权限拒绝处理逻辑,但问题未解决。
原代码
private ImageView imageViewSettings; private int permissionDeniedCount = 0; private ActivityResultLauncher<String> requestPermissionLauncher; private ActivityResultLauncher<Intent> pickImageLauncher; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_settings, container, false); imageViewSettings = view.findViewById(R.id.imageViewSettings); Button buttonAccessPhotos = view.findViewById(R.id.buttonAccessPhotos); requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { Log.d(TAG, "Permission result: " + isGranted); if (isGranted) { Toast.makeText(getContext(), "Permission allowed", Toast.LENGTH_SHORT).show(); accessPhotos(); } else { permissionDeniedCount++; if (permissionDeniedCount >= 2) { // Show toast and guide the user to the app settings Toast.makeText(getContext(), "Permission denied twice. Please enable from settings.", Toast.LENGTH_LONG).show(); Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getContext().getPackageName())); startActivity(intent); } else { Toast.makeText(getContext(), "Permission denied", Toast.LENGTH_SHORT).show(); } } }); pickImageLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == FragmentActivity.RESULT_OK && result.getData() != null && result.getData().getData() != null) { Uri selectedImage = result.getData().getData(); imageViewSettings.setImageURI(selectedImage); } }); buttonAccessPhotos.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "Access Photos button clicked"); checkPermission(); } }); return view; } private void checkPermission() { FragmentActivity activity = getActivity(); if (activity == null) { Log.e(TAG, "Activity is null"); return; } int permissionState = ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE); Log.d(TAG, "Permission state: " + permissionState); if (permissionState != PackageManager.PERMISSION_GRANTED) { if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) { Log.d(TAG, "Showing permission rationale"); Toast.makeText(getContext(), "We need access to your photos to select an image.", Toast.LENGTH_LONG).show(); requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE); } else { if (permissionDeniedCount >= 2) { Log.d(TAG, "Permission denied permanently. Guiding user to settings."); Toast.makeText(getContext(), "Permission denied twice. Please enable from settings.", Toast.LENGTH_LONG).show(); Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getContext().getPackageName())); startActivity(intent); } else { Log.d(TAG, "Requesting permission"); requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE); } } } else { // Permission has already been granted Log.d(TAG, "Permission already granted"); accessPhotos(); } } private void accessPhotos() { Log.d(TAG, "Accessing photos"); Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); pickImageLauncher.launch(intent); }
问题分析
- 权限计数逻辑缺陷:依赖
permissionDeniedCount判断永久拒绝不准确,若用户第一次就勾选"不再询问",计数仍为0,系统会自动拒绝权限,导致后续逻辑混乱;且Fragment重建(如屏幕旋转)会重置计数,状态丢失。 - Context空指针风险:调用
startActivity时使用getContext(),若Fragment已与Activity分离,getContext()会返回null,引发崩溃。 - Android版本权限适配缺失:Android 13(API 33)及以上已废弃
READ_EXTERNAL_STORAGE,需使用READ_MEDIA_IMAGES权限,原代码未适配导致权限申请无效。 - 永久拒绝判断错误:未正确利用
shouldShowRequestPermissionRationale的返回值判断用户是否勾选"不再询问",而是依赖计数,逻辑不准确。
修复方案
1. 适配Android 13+权限
在Manifest文件中添加分版本权限声明:
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
在代码中根据系统版本动态获取对应权限:
private String getPhotoPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return Manifest.permission.READ_MEDIA_IMAGES; } else { return Manifest.permission.READ_EXTERNAL_STORAGE; } }
2. 修复永久拒绝判断逻辑
移除permissionDeniedCount,改用SharedPreferences记录权限请求历史,结合shouldShowRequestPermissionRationale判断永久拒绝:
private void checkPermission() { String permission = getPhotoPermission(); int permissionState = ContextCompat.checkSelfPermission(requireContext(), permission); if (permissionState != PackageManager.PERMISSION_GRANTED) { if (shouldShowRequestPermissionRationale(permission)) { Toast.makeText(requireContext(), "需要访问相册来选择图片", Toast.LENGTH_LONG).show(); requestPermissionLauncher.launch(permission); } else { SharedPreferences prefs = requireContext().getSharedPreferences("app_prefs", Context.MODE_PRIVATE); boolean hasRequested = prefs.getBoolean("has_requested_photo_permission", false); if (hasRequested) { // 用户已拒绝并勾选不再询问,引导至设置 Toast.makeText(requireContext(), "权限已被永久拒绝,请在设置中开启", Toast.LENGTH_LONG).show(); openAppSettings(); } else { // 首次请求权限,标记已请求 prefs.edit().putBoolean("has_requested_photo_permission", true).apply(); requestPermissionLauncher.launch(permission); } } } else { accessPhotos(); } }
3. 修复Context空指针问题
封装打开设置页面的方法,使用requireContext()确保Context有效,并添加FLAG_ACTIVITY_NEW_TASK避免栈问题:
private void openAppSettings() { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + requireContext().getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); }
4. 优化权限回调逻辑
更新requestPermissionLauncher的回调,移除计数逻辑,直接根据权限状态处理:
requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { Log.d(TAG, "Permission result: " + isGranted); if (isGranted) { Toast.makeText(requireContext(), "权限已允许", Toast.LENGTH_SHORT).show(); accessPhotos(); } else { if (!shouldShowRequestPermissionRationale(getPhotoPermission())) { // 用户勾选不再询问,引导至设置 Toast.makeText(requireContext(), "权限已被拒绝,请在设置中开启", Toast.LENGTH_LONG).show(); openAppSettings(); } else { Toast.makeText(requireContext(), "权限已拒绝", Toast.LENGTH_SHORT).show(); } } });
修复后完整代码
private ImageView imageViewSettings; private ActivityResultLauncher<String> requestPermissionLauncher; private ActivityResultLauncher<Intent> pickImageLauncher; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_settings, container, false); imageViewSettings = view.findViewById(R.id.imageViewSettings); Button buttonAccessPhotos = view.findViewById(R.id.buttonAccessPhotos); requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { Log.d(TAG, "Permission result: " + isGranted); if (isGranted) { Toast.makeText(requireContext(), "权限已允许", Toast.LENGTH_SHORT).show(); accessPhotos(); } else { if (!shouldShowRequestPermissionRationale(getPhotoPermission())) { Toast.makeText(requireContext(), "权限已被永久拒绝,请在设置中开启", Toast.LENGTH_LONG).show(); openAppSettings(); } else { Toast.makeText(requireContext(), "权限已拒绝", Toast.LENGTH_SHORT).show(); } } }); pickImageLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == FragmentActivity.RESULT_OK && result.getData() != null && result.getData().getData() != null) { Uri selectedImage = result.getData().getData(); imageViewSettings.setImageURI(selectedImage); } }); buttonAccessPhotos.setOnClickListener(v -> { Log.d(TAG, "Access Photos button clicked"); checkPermission(); }); return view; } private void checkPermission() { String permission = getPhotoPermission(); int permissionState = ContextCompat.checkSelfPermission(requireContext(), permission); if (permissionState != PackageManager.PERMISSION_GRANTED) { if (shouldShowRequestPermissionRationale(permission)) { Toast.makeText(requireContext(), "需要访问相册来选择图片", Toast.LENGTH_LONG).show(); requestPermissionLauncher.launch(permission); } else { SharedPreferences prefs = requireContext().getSharedPreferences("app_prefs", Context.MODE_PRIVATE); boolean hasRequested = prefs.getBoolean("has_requested_photo_permission", false); if (hasRequested) { openAppSettings(); } else { prefs.edit().putBoolean("has_requested_photo_permission", true).apply(); requestPermissionLauncher.launch(permission); } } } else { accessPhotos(); } } private String getPhotoPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return Manifest.permission.READ_MEDIA_IMAGES; } else { return Manifest.permission.READ_EXTERNAL_STORAGE; } } private void openAppSettings() { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + requireContext().getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } private void accessPhotos() { Log.d(TAG, "Accessing photos"); Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); pickImageLauncher.launch(intent); }
内容的提问来源于stack exchange,提问作者MAHARSH RAVAL
相关产品推荐
相关产品推荐

