Keywords: Android Permissions | Runtime Permissions | Marshmallow | Permission Request | Dangerous Permissions
Abstract: This article provides an in-depth analysis of the runtime permission model introduced in Android 6.0 Marshmallow. It covers the permission request workflow, code implementation, and best practices, including permission checks, request dialogs, and result handling. Refactored code examples demonstrate how to correctly implement dynamic requests for dangerous permissions, ensuring optimal user experience whether permissions are granted or denied.
Overview of the Runtime Permission Model
Android 6.0 Marshmallow (API Level 23) introduced the runtime permission mechanism, requiring apps to dynamically request user authorization when accessing sensitive data or features. Unlike previous versions where all permissions were granted at install time, the new model categorizes permissions into normal and dangerous types, with the latter requiring explicit runtime requests. This change enhances user control over privacy and necessitates adjustments in permission handling logic by developers.
Basic Workflow for Permission Requests
The runtime permission request follows a standard workflow: first, check if the permission is already granted; if not, display an explanatory UI if needed, then request the permission and handle the user's response. Key steps include:
- Check current permission status: Use the
ContextCompat.checkSelfPermission()method. - Determine if explanation is needed: Call
ActivityCompat.shouldShowRequestPermissionRationale(). - Request permission: Trigger the system dialog via
ActivityCompat.requestPermissions(). - Handle permission results: Execute appropriate actions based on grant status in the
onRequestPermissionsResult()callback.
Detailed Code Implementation
The following code, refactored based on Q&A data, illustrates the complete permission request process. Assume the app requires the READ_EXTERNAL_STORAGE permission:
// Check permission status
int permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
// Determine if explanation is needed
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
// Display explanatory UI describing why the permission is needed
showPermissionRationale();
} else {
// Directly request the permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_CODE_READ_STORAGE);
}
} else {
// Permission already granted, perform related operation
performStorageOperation();
}
// Handle permission request results
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_READ_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, continue operation
performStorageOperation();
} else {
// Permission denied, gracefully degrade
Toast.makeText(this, "Storage permission denied, some features unavailable", Toast.LENGTH_SHORT).show();
disableStorageFeatures();
}
}
}
Best Practices for Permission Requests
Based on the reference article, adhere to the following principles when requesting permissions:
- Contextual Requests: Request permissions when the user triggers a feature that requires them, avoiding bulk requests at app startup.
- Provide Explanation: Use UI to explain why a permission is needed if its purpose isn't clear, building user trust.
- Graceful Degradation: If a permission is denied, disable related features without blocking the app, ensuring core functionality remains accessible.
- Avoid Assumptions: Do not rely on permission grouping behaviors; always check permission status before each operation.
Advanced Features and Considerations
Android 11 and later introduce one-time permission grants, allowing users to choose "Only this time". Additionally, if a user denies a permission multiple times, the system may stop showing the request dialog, treating it as a permanent denial. Thus, developers must carefully time requests to avoid losing the ability to re-request.
Testing and Debugging
Use ADB commands to simulate permission states, e.g., adb shell pm grant PACKAGE_NAME PERMISSION_NAME to grant a permission, and adb shell pm revoke PACKAGE_NAME PERMISSION_NAME to revoke it. This aids in verifying the correctness of permission handling logic during development.