Keywords: Android File Writing | EROFS Error | Storage Permission Management
Abstract: This article provides an in-depth exploration of the common EROFS (Read-only file system) error in Android development, analyzing its root cause as applications attempting to write to root directories without proper permissions. By comparing the access mechanisms of internal and external storage, it details how to correctly use getFilesDir() and getExternalFilesDir() methods to obtain writable paths. The article also discusses best practices for permission management, including proper usage scenarios for WRITE_EXTERNAL_STORAGE permission, and presents alternatives for avoiding serialization of large data, such as using static data members for temporary storage. Finally, it clarifies common misconceptions about SD card slots, emphasizing the characteristics of external storage in modern Android devices.
Root Cause Analysis of EROFS Error
In Android development, when attempting to create files using FileOutputStream, developers often encounter FileNotFoundException accompanied by EROFS (Read-only file system) error messages. The core issue lies in applications trying to write to directories without write permissions. Specifically, paths like /2ozjfFQzwv in error messages indicate the program is attempting to write to the root directory of internal storage, which Android system restricts for security reasons.
Correct File Path Selection Strategy
Android provides two main writable storage areas: internal storage and external storage. Internal storage is private to the application and accessible without additional permissions; external storage may require WRITE_EXTERNAL_STORAGE permission, depending on Android version and storage location.
To properly create files, appropriate file paths must be obtained first. Here are two recommended approaches:
// Method 1: Using internal storage directory
File internalFile = new File(getFilesDir(), "filename.txt");
FileOutputStream internalStream = new FileOutputStream(internalFile);
// Method 2: Using application-private directory in external storage
File externalFile = new File(getExternalFilesDir(null), "filename.txt");
// On Android 6.0 and above, dynamic request for WRITE_EXTERNAL_STORAGE permission may be needed
FileOutputStream externalStream = new FileOutputStream(externalFile);
getFilesDir() returns the application's private directory in internal storage, which requires no special permissions for read/write operations. getExternalFilesDir() returns the application's private directory in external storage; since Android 4.4, applications can access their own external private directories without permissions.
Important Considerations for Permission Management
Several key concepts need clarification regarding permission management:
- There is no
WRITE_INTERNAL_STORAGEpermission in Android. Access to internal storage is automatically managed by the system, with applications only able to access their own private directories. - The
WRITE_EXTERNAL_STORAGEpermission is primarily for accessing public areas of external storage, such as DCIM, Downloads directories. For an application's own external private directory (obtained viagetExternalFilesDir()), this permission is typically not required since Android 4.4. - In Android 6.0 (API level 23) and above, dangerous permissions (including
WRITE_EXTERNAL_STORAGE) must be requested dynamically at runtime.
Alternative Solutions for Large Data Transfer
When transferring large data (such as images) between Activities, direct serialization may be impractical. Besides writing to files and reading them back, consider these alternatives:
// Using static data members for temporary storage
public class DataHolder {
private static Bitmap sharedImage;
public static void setImage(Bitmap image) {
sharedImage = image;
}
public static Bitmap getImage() {
return sharedImage;
}
}
// In sending Activity
DataHolder.setImage(largeBitmap);
// In receiving Activity
Bitmap receivedImage = DataHolder.getImage();
This approach avoids file I/O operations but requires attention to memory management and lifecycle issues. Static data members remain in memory until the application process ends, making them unsuitable for storing large amounts of data or data requiring long-term preservation.
Modern Understanding of External Storage
An important clarification about external storage is that in modern Android devices, "external storage" typically refers to built-in shared storage space, not physical SD cards. Since mid-2010, most Android devices have included external storage as part of onboard flash memory rather than removable media. This means:
- Vast majority of Android users have accessible external storage space
- Presence of SD card slots generally doesn't affect external storage availability
- Applications should prioritize using
getExternalFilesDir()over relying on removable SD cards
This design ensures consistent application experience across different devices while maintaining appropriate security boundaries.
Best Practices Summary
Based on the above analysis, we summarize the following best practices:
- Always use
Fileobjects with appropriate directory paths when creatingFileOutputStream - Prioritize internal storage (
getFilesDir()) for private data storage, requiring no permissions - Use application-private directories in external storage (
getExternalFilesDir()) for shared or larger files - Properly handle permission requests, especially runtime permissions for Android 6.0+
- For temporary large data transfer, consider memory-based solutions like static data members
- Understand modern Android storage architecture to avoid over-reliance on physical SD cards
By following these principles, developers can avoid EROFS errors while ensuring application compatibility and security across different Android devices and versions.