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

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);
}

问题分析

  1. 权限计数逻辑缺陷:依赖permissionDeniedCount判断永久拒绝不准确,若用户第一次就勾选"不再询问",计数仍为0,系统会自动拒绝权限,导致后续逻辑混乱;且Fragment重建(如屏幕旋转)会重置计数,状态丢失。
  2. Context空指针风险:调用startActivity时使用getContext(),若Fragment已与Activity分离,getContext()会返回null,引发崩溃。
  3. Android版本权限适配缺失:Android 13(API 33)及以上已废弃READ_EXTERNAL_STORAGE,需使用READ_MEDIA_IMAGES权限,原代码未适配导致权限申请无效。
  4. 永久拒绝判断错误:未正确利用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 05:28:11