Comprehensive Guide to Android External Storage Permissions: Solving READ_EXTERNAL_STORAGE Issues

Nov 28, 2025 · Programming · 10 views · 7.8

Keywords: Android | Permission Management | External Storage | Runtime Permissions | Music Player

Abstract: This article provides an in-depth exploration of common issues with READ_EXTERNAL_STORAGE permissions in Android applications. Through analysis of a real-world music player case study, it explains the distinction between permission declaration and runtime requests, offers code examples compatible with different Android versions, and discusses the evolution of permission models and best practices.

Introduction

Accessing media files on external storage devices is a common requirement in Android application development, particularly when building music players, gallery apps, or file managers. However, many developers encounter various issues when using the READ_EXTERNAL_STORAGE permission, leading to application crashes or failure to read files properly. This article analyzes the root causes of these problems through a practical case study and provides effective solutions.

Problem Analysis

In the provided Q&A data, a developer attempted to build a simple music player application but encountered a SecurityException when accessing audio files from external storage. The error message clearly stated: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=27696, uid=10059 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission(). This indicates that although the application declared the permission in AndroidManifest.xml, it did not obtain authorization at runtime.

The developer tried multiple approaches, including adjusting the permission declaration location, adding maxSdkVersion attributes, setting the android:exported flag, and using grantUriPermission, but none succeeded. These attempts reflect a misunderstanding of Android's permission model, particularly the runtime permission mechanism.

Evolution of Android Permission Model

Android's permission management has undergone several significant changes. Before Android 6.0 (API level 23), applications only needed to declare permissions in the manifest file, with users granting all permissions at installation time. This model posed privacy and security risks, prompting Google to introduce the runtime permission model in Android 6.0.

Runtime permissions categorize permissions into two types: normal permissions and dangerous permissions. Normal permissions (such as INTERNET) are automatically granted at installation, while dangerous permissions (such as READ_EXTERNAL_STORAGE) must be explicitly granted by the user at runtime. This change requires developers to actively request permissions in code and handle user authorization results.

Solutions

Method 1: Lower targetSdkVersion

This is a temporary solution that bypasses runtime permission requirements by setting the application's targetSdkVersion to 22 or lower. The system will use the older permission model, granting all declared permissions at installation. However, this approach is not recommended for production environments as it cannot leverage new system security features and may become ineffective in future versions.

Method 2: Implement Runtime Permission Requests

This is the correct and recommended solution. Developers need to check permission status and request authorization appropriately before accessing protected resources. Below is a code example based on the best answer:

private static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;

public void initialize() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.READ_EXTERNAL_STORAGE)) {
            // Explain to the user why the permission is needed
            showExplanationDialog();
        } else {
            // Request the permission directly
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
        }
    } else {
        // Permission has been granted, perform the operation
        loadMediaFiles();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Permission granted
            loadMediaFiles();
        } else {
            // Permission denied
            handlePermissionDenied();
        }
    }
}

private void loadMediaFiles() {
    ContentResolver contentResolver = getContentResolver();
    Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    String[] projection = {
            MediaStore.Audio.Media._ID,
            MediaStore.Audio.Media.TITLE,
            MediaStore.Audio.Media.ARTIST,
            MediaStore.Audio.Media.DATA
    };
    
    Cursor cursor = contentResolver.query(uri, projection, null, null, null);
    if (cursor != null) {
        while (cursor.moveToNext()) {
            String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
            String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
            String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
            // Process audio file information
        }
        cursor.close();
    }
}

Best Practices for Permission Handling

When implementing runtime permissions, follow these best practices:

  1. Request Permissions Appropriately: Request permissions when users perform actions that require them, not at application startup.
  2. Provide Explanations: Use shouldShowRequestPermissionRationale to determine if an explanation is needed when users have previously denied the permission.
  3. Handle Denials Gracefully: When users deny permissions, implement graceful degradation rather than allowing the application to crash.
  4. Test Different Scenarios: Ensure the application works correctly when permissions are granted, denied, or permanently denied.

Storage Permission Changes in Android 10+

Starting with Android 10 (API level 29), Google introduced Scoped Storage, further restricting application access to external storage. Under Scoped Storage, applications can only access their private directories and specific types of media files by default, without requiring the READ_EXTERNAL_STORAGE permission.

For cases requiring access to media files created by other applications, use the new media permissions: READ_MEDIA_AUDIO, READ_MEDIA_IMAGES, and READ_MEDIA_VIDEO. These permissions, introduced in Android 13 (API level 33), replace READ_EXTERNAL_STORAGE for media file access control.

Compatibility Considerations

To ensure applications work correctly across different Android versions, developers must carefully handle permission compatibility:

private void checkStoragePermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        // Android 13+ uses new media permissions
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_AUDIO)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_MEDIA_AUDIO},
                    MY_PERMISSIONS_REQUEST_READ_MEDIA_AUDIO);
        } else {
            loadMediaFiles();
        }
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        // Android 6.0-12 uses READ_EXTERNAL_STORAGE
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                    MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
        } else {
            loadMediaFiles();
        }
    } else {
        // Android 5.1 and below, permissions are automatically granted at installation
        loadMediaFiles();
    }
}

Conclusion

Properly handling READ_EXTERNAL_STORAGE and other storage-related permissions is crucial in Android application development. By understanding the runtime permission model, following best practices, and considering version compatibility, developers can build secure and user-friendly applications. As the Android system continues to evolve, storage permission management may undergo further changes, making it essential to stay informed about the latest development practices.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.