Skip to content

Android External Storage — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about Android External Storage. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Your app can't access external storage on Android 11+, or your files created with getExternalStoragePublicDirectory() aren't visible in the file manager.

Wrong Approach ❌

// Using deprecated Environment.getExternalStoragePublicDirectory()
val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val file = File(dir, "myapp_report.pdf")
// Requires WRITE_EXTERNAL_STORAGE which doesn't work on API 30+

Output: AccessDeniedException on Android 11+. Files not visible.

Right Approach ✅

class ExternalFileManager(private val context: Context) {

    // App-specific external storage — no permissions needed (API 19+)
    fun saveToAppExternal(filename: String, content: String) {
        val dir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
        if (dir != null) {
            val file = File(dir, filename)
            file.writeText(content)
        }
    }

    // App-specific cache on external storage
    fun saveToAppExternalCache(filename: String, data: ByteArray) {
        val cacheDir = context.externalCacheDir
        if (cacheDir != null) {
            File(cacheDir, filename).writeBytes(data)
        }
    }

    // For sharing files via FileProvider
    fun getShareableUri(file: File): Uri {
        return FileProvider.getUriForFile(
            context,
            "${context.packageName}.fileprovider",
            file
        )
    }

    // Media store for public files (API 29+)
    fun saveToPublicDownloads(filename: String, content: ByteArray) {
        val contentValues = ContentValues().apply {
            put(MediaStore.Downloads.DISPLAY_NAME, filename)
            put(MediaStore.Downloads.MIME_TYPE, "application/pdf")
            put(MediaStore.Downloads.IS_PENDING, 1)
        }

        val uri = context.contentResolver.insert(
            MediaStore.Downloads.EXTERNAL_CONTENT_URI,
            contentValues
        )

        uri?.let {
            context.contentResolver.openOutputStream(it)?.use { output ->
                output.write(content)
            }

            contentValues.clear()
            contentValues.put(MediaStore.Downloads.IS_PENDING, 0)
            context.contentResolver.update(it, contentValues, null, null)
        }
    }

    // Check if external storage is available
    fun isExternalStorageAvailable(): Boolean {
        return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
    }
}

// FileProvider XML (res/xml/file_paths.xml)
// <paths>
//     <external-files-path name="documents" path="Documents/" />
// </paths>

Output: Files saved correctly with proper API for each Android version.

Prevention

  • Use getExternalFilesDir() for app-specific files (no permissions needed).
  • Use MediaStore for public files on API 29+.
  • Use FileProvider for sharing files with other apps.
  • Don't request WRITE_EXTERNAL_STORAGE on API 30+ — it's mostly ineffective.

Common Mistakes with storage external

  1. Using return to exit a function early instead of wrapping a pure value in the monad
  2. Mixing let bindings with <- bindings in do notation, producing type errors
  3. Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors

These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### What is scoped storage?

Scoped storage (Android 10+) limits app access to external storage. Apps can only access their app-specific directory and media files they created via MediaStore.

### How do I access files from other apps?

Use Intent(Intent.ACTION_OPEN_DOCUMENT) to let the user pick files via the system file picker. You get a content URI with temporary read access.

### Why are my files not visible in the file manager?

App-specific files (getExternalFilesDir()) are hidden from the file manager by default. Use MediaStore to make files visible in the gallery/downloads app.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro