Keywords: Android Permissions | Marshmallow | WRITE_EXTERNAL_STORAGE | Runtime Permissions | Permission Handling
Abstract: This article provides an in-depth analysis of the runtime permission mechanism introduced in Android 6.0 Marshmallow, focusing on the handling of WRITE_EXTERNAL_STORAGE permission in download functionality. By comparing permission differences between Lollipop and Marshmallow, it details the complete implementation process of permission checking, requesting, and callback handling, and offers compatibility solutions to help developers resolve storage permission issues when upgrading from Lollipop to Marshmallow.
Runtime Permission Mechanism Overview
Android 6.0 Marshmallow introduced significant changes to the permission model, dividing permissions into normal permissions and dangerous permissions. WRITE_EXTERNAL_STORAGE falls under dangerous permissions and requires dynamic runtime requests in Marshmallow and later versions, unlike Lollipop where permissions were granted once during installation. This change significantly enhances user control over privacy and data security.
Root Cause Analysis of Permission Issues
In the Lollipop system, applications received all permissions declared in AndroidManifest.xml during installation. However, in Marshmallow, even if WRITE_EXTERNAL_STORAGE permission is declared in the manifest file, the application still needs to explicitly request this permission from the user during first use. This is the fundamental reason why download functionality that worked normally in Lollipop crashes after upgrading to Marshmallow.
Complete Permission Handling Process
To implement Marshmallow-compatible storage permission handling, the following complete process must be followed:
Permission Checking Mechanism
Before performing any file operations, it is essential to first check whether the required permissions have been granted. This can be achieved using the checkSelfPermission() method:
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
Log.v(TAG, "Permission is granted");
// Execute file writing logic
return true;
}
Permission Request Process
When it is detected that the permission has not been granted, a permission request must be initiated to the user:
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
The system will display a standard permission request dialog where the user can choose to allow or deny the permission.
Version Compatibility Handling
Since the runtime permission mechanism only applies to Marshmallow and later versions, version checking logic must be added:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Execute Marshmallow-specific permission checks
} else {
// For earlier versions, permissions were automatically granted during installation
}
Complete Permission Management Implementation
Below is a complete implementation example of a permission checking and requesting method:
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
Log.v(TAG, "Permission is granted");
return true;
} else {
Log.v(TAG, "Permission is revoked");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
} else {
Log.v(TAG, "Permission is granted");
return true;
}
}
Permission Request Result Handling
The Activity must implement the onRequestPermissionsResult callback method to handle the user's permission choices:
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.v(TAG, "Permission: " + permissions[0] + " was " + grantResults[0]);
// Resume tasks requiring this permission
}
}
Practical Application Scenario Analysis
In download manager scenarios, the correct approach is to perform permission checks before calling DownloadManager.enqueue(). If the permission has not been granted, the download operation should be paused and permission requested. Only after the user grants permission in the permission callback should the download task proceed.
Best Practice Recommendations
1. Perform permission checks during application startup or when using permission-required features for the first time
2. Clearly explain to users why the permission is needed to enhance user experience
3. Handle cases where users deny permissions by providing graceful degradation solutions
4. Regularly test permission behavior across different Android versions
Conclusion
While Android Marshmallow's runtime permission mechanism increases development complexity, it provides crucial protection for user privacy. Through proper permission checking, requesting, and handling processes, developers can build applications that are both secure and user-friendly. Understanding and correctly implementing these mechanisms is essential for ensuring stable application operation on Marshmallow and later versions.