Android Storage: Comprehensive Guide with Simplified Explanation

Android storage has undergone major changes over time, mainly to enhance user privacy and storage management. Since Android is based on Linux OS, it follows a similar file system structure.

Types of Storage in Android Devices

Storage in any device can be categorized into different forms like hard disks, external drives, or cloud storage. However, in Android, the two main types of storage are:

  1. Device Internal Storage: This is the built-in flash memory attached to the motherboard, which stores the operating system (OS) files, system data, and app data.
  2. Device External Storage: This refers to storage that is outside the core system partition, which can either be a part of the internal storage or a removable SD card.

Understanding Internal and External Storage in Android

Initially, external storage meant removable SD cards because devices had limited internal space. But as manufacturers started increasing built-in storage, Android adapted by partitioning the internal storage into:

  • Internal Storage: Primary storage area for system files and app data.
  • External Storage: A separate partition within internal storage that acts as a shared storage space for media files and user content.
  • Secondary Storage: Any removable SD card attached to the device.

When you explore storage directories in Android Studio’s Device Explorer, you will notice more than 35 different directories, each serving a specific purpose.

Difference Between App Internal and App External Storage

1. App Internal Storage

This is a private storage space within the device’s internal storage, meant for app-specific data. Here are some key points:

  • Privacy: Data stored here is only accessible by the app; other apps cannot access it.
  • Limited Space: Apps get very limited storage in this area, so storing large files is not recommended.
  • No Permissions Required: Since this is app-specific, no special permissions are needed to access it.
  • Invisible to Users: Users cannot directly see or manage files stored here through file managers or gallery apps.
  • Use Cases: Used for storing configuration files, user preferences, and temporary data.
  • Storage Location: data/data/{your_app_package}/

Now, let’s explore the operations you can perform on internal storage:

1) Save data in internal storage:

Here’s how you can write data to a file in internal storage:

fun writeToFile(context: Context, filename: String, data: String) {
    context.openFileOutput(filename, Context.MODE_PRIVATE).use {
        it.write(data.toByteArray())
    }
}

In this function, openFileOutput() is used to create a file in the internal storage. The MODE_PRIVATE argument means the file will be accessible by only your app. The use function is a Kotlin extension function on Closeable, which automatically closes the stream when finished.

2) Load data from internal storage:

Reading data from a file in internal storage can be done as follows:

fun readFromFile(context: Context, filename: String): String {
    return context.openFileInput(filename).bufferedReader().use { it.readText() }
}

Here, openFileInput() is used to open the file, and bufferedReader().use { it.readText() } is used to read the file content as a string.

3) Delete data from internal storage:

Deleting a file from internal storage is straightforward:

fun deleteFile(context: Context, filename: String) {
    context.deleteFile(filename)
}

The deleteFile() function is used to remove a file from the internal storage

2. App External Storage

This is a separate storage directory created for an app within the external storage partition.

  • App-Specific Storage: Files stored here belong to the app but are accessible to the user.
  • Permissions:
    • Before API 28 (Android 9) β†’ Apps required READ/WRITE permissions.
    • From API 29 (Android 10) onwards β†’ Scoped Storage was introduced, allowing apps to access their own directory without permission.
  • Visibility: Files stored here are visible to users via file managers or gallery apps.
  • Accessibility: Other apps can access these files with proper permissions.
  • Use Cases: Used for large media files, cached data, and app downloads.
  • Storage Location: storage/emulated/0/Android/data/{your_app_package}/

1) Save data in external storage:

To write data to external storage, you must request the appropriate permissions from the user. From Android 6.0 (API level 23), you need to check for and request permissions at runtime. For external storage, typically you need to request WRITE_EXTERNAL_STORAGE permission. However, if you’re targeting Android 10 (API level 29) or above and using getExternalFilesDir(), you don’t need to request this permission because you’re writing to your app’s specific directory.

Also, check if the external storage is available and writable. Then, you can use FileOutputStream to write data. Here’s an example:

fun writeToFile(context: Context, filename: String, data: String) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
        ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != 
           PackageManager.PERMISSION_GRANTED) {
        return
    }

    if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
        val file = File(context.getExternalFilesDir(null), filename)
        FileOutputStream(file).use {
            it.write(data.toByteArray())
        }
    }
}

In this function, before writing to the external storage, it checks if the app is running on Android 10 (API level 29) or higher. If it’s running on a lower version, it checks if the WRITE_EXTERNAL_STORAGE permission has been granted. If the permission isn’t granted, you should request it. The actual permission request is commented out because it usually involves UI interaction and should be handled in an Activity or Fragment where you can override onRequestPermissionsResult to handle the callback.

getExternalFilesDir(null) is used to get a File representing your app’s private files directory in external storage. The null argument means you’re not requesting a specific sub-directory, but you could request directories for specific types of files like Environment.DIRECTORY_PICTURES.

2) Load Data from External Storage:

Reading data from a file in external storage is similar to writing. You need to check for permissions and if the external storage is available to read. Then, you can use FileInputStream to read data:

fun readFromFile(context: Context, filename: String): String? {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
        ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        return null
    }

    if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED || Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED_READ_ONLY) {
        val file = File(context.getExternalFilesDir(null), filename)
        return FileInputStream(file).bufferedReader().use { it.readText() }
    }
    return null
}

Here, FileInputStream(file).bufferedReader().use { it.readText() } is used to read the file content as a string. Also, note that when loading data, we need the READ_EXTERNAL_STORAGE permission if our app is running on Android 9 or lower.

3) Delete Data from External Storage:

To delete a file from external storage, check if the WRITE_EXTERNAL_STORAGE permission has been granted and if the storage is available, then call delete() on the File object:

fun deleteFile(context: Context, filename: String): Boolean {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q &&
        ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        return
    }

    if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
        val file = File(context.getExternalFilesDir(null), filename)
        return file.delete()
    }
    return false
}

Scoped Storage: Enhanced Privacy in Android

Scoped Storage was introduced to improve data security and privacy. It means that apps now have restricted access to external storage and can only modify files within their own specific directory.

Key Changes:

  • Apps cannot access files in other directories without user consent.
  • Requires MediaStore API or Storage Access Framework (SAF) to interact with shared storage.
  • Prevents unauthorized access to other apps’ data, enhancing privacy and security.

Shared Storage: Publicly Accessible Storage

This storage type is accessible to all apps and is commonly used for media files and documents.

  • Public Storage: Stores files like music, videos, images, and documents.
  • Permissions:
    • Requires READ/WRITE permissions for access.
    • Permissions vary based on API level.
  • Visibility: Files can be accessed via file managers and gallery apps.
  • Accessibility: Can be accessed using:
    • MediaStore API (for indexed media files like images, videos, and music).
    • Storage Access Framework (SAF) (for accessing and managing all types of files).
  • Use Cases: Used by music players, gallery apps, document editors, etc.

MediaStore API & Storage Access Framework (SAF) Explained

When dealing with shared storage (like photos, videos, music, or documents), Android provides two main ways to access and manage files:

  1. MediaStore API – Best for structured media files (images, videos, audio).
  2. Storage Access Framework (SAF) – Best for general file access (PDFs, text files, and all non-media documents).

Both APIs ensure better security and restrict unnecessary access to the user’s storage.


πŸ“Œ MediaStore API – Best for Media Files

MediaStore is Android’s system-level database that indexes all media files stored on the device (images, videos, and audio). It allows apps to efficiently read, write, and manage media files.

βœ… When to Use MediaStore?

βœ” When you need to store or retrieve images, videos, or audio files.
βœ” If you want faster access to media files with proper indexing.
βœ” When you don’t need to show a file picker to users.

πŸ›  CRUD Operations using MediaStore

πŸ“ Create (Save a new image)

val contentValues = ContentValues().apply {
    put(MediaStore.Images.Media.DISPLAY_NAME, "my_image.jpg")
    put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
    put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/MyApp")
}

val uri = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
uri?.let {
    context.contentResolver.openOutputStream(it)?.use { outputStream ->
        val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.sample)
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
    }
}

πŸ“ Read (Fetch all images from MediaStore)

val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME)
val cursor = context.contentResolver.query(
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
    projection, 
    null, null, null
)

cursor?.use {
    val idColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
    val nameColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
    
    while (it.moveToNext()) {
        val id = it.getLong(idColumn)
        val name = it.getString(nameColumn)
        val contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
        Log.d("MediaStore", "Image: $name, Uri: $contentUri")
    }
}

πŸ“ Update (Rename an image)

val values = ContentValues().apply {
    put(MediaStore.Images.Media.DISPLAY_NAME, "new_image_name.jpg")
}
context.contentResolver.update(uri, values, null, null)

πŸ“ Delete (Remove an image)

context.contentResolver.delete(uri, null, null)

πŸ“Œ Storage Access Framework (SAF) – Best for File Picker Access

SAF allows users to pick, open, edit, and save files across all storage locations (including cloud storage like Google Drive).

βœ… When to Use SAF?

βœ” When you need user-selected file access (PDFs, docs, text files, etc.).
βœ” If you want to edit files from different storage providers (Drive, SD card, etc.).
βœ” When you need a file picker UI for user-friendly file selection.

πŸ›  CRUD Operations using SAF

πŸ“ Create (Save a file using SAF)

val context = LocalContext.current
val createDocumentLauncher = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { uri ->
    uri?.let {
        context.contentResolver.openOutputStream(it)?.use { outputStream ->
            outputStream.write("Hello, this is a new file.".toByteArray())
        }
    }
}

// Trigger this when the user wants to create a file
Button(onClick = { createDocumentLauncher.launch("myfile.txt") }) {
    Text("Create File")
}

πŸ“ Read (Open a file using SAF)

val context = LocalContext.current
val openDocumentLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
    uri?.let {
        context.contentResolver.openInputStream(it)?.bufferedReader()?.use { reader ->
            val content = reader.readText()
            Log.d("SAF", "File Content: $content")
        }
    }
}

// Trigger this when the user wants to open a file
Button(onClick = { openDocumentLauncher.launch(arrayOf("text/plain")) }) {
    Text("Open File")
}

πŸ“ Delete (Remove a file using SAF)

val context = LocalContext.current
val deleteDocumentLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
    uri?.let {
        DocumentsContract.deleteDocument(context.contentResolver, it)
    }
}

// Trigger this when the user wants to delete a file
Button(onClick = { deleteDocumentLauncher.launch(arrayOf("text/plain")) }) {
    Text("Delete File")
}

Conclusion

Understanding Android storage is crucial for efficient file handling and data security. With the introduction of Scoped Storage and MediaStore API, apps now follow stricter privacy rules, ensuring better user data protection.

πŸ“Œ Key Takeaways:

  • App Internal Storage β†’ Private, no permissions needed, not accessible by users.
  • App External Storage β†’ Public, requires permissions, used for large files.
  • Scoped Storage β†’ Restricts access to external storage for security.
  • Shared Storage β†’ Common for media and documents, requires MediaStore or SAF.

By understanding these concepts, you can build better, more secure Android apps that comply with the latest storage policies and best practices. πŸš€

This is GitHub gist link for complete project in the video here

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top