Table of Contents
Understanding Internal Storage on Android
Internal storage is a private area on the device where your app can save files. Only your app can normally access these files. This makes internal storage the simplest and safest place to keep user data that should not be shared with other apps.
When the user uninstalls your app, the system removes your internal storage files automatically. You do not have to clean them up yourself in most cases.
Where Internal Storage Lives in an App
Every installed app gets its own internal files directory. In code, you usually access it using a Context object, which you have in activities, services, and many other places.
The most common directory is returned by filesDir. In Kotlin you can write something like:
val directory = filesDir
val path = directory.absolutePathThis directory is unique to your app. Other apps cannot read or write here without special permissions that normal apps do not get.
You can also get a cache directory using cacheDir. This is for temporary files that the system may delete when it needs space.
Creating and Writing Files in Internal Storage
To write a text file into internal storage, you usually open a file output stream and then write bytes or text. One of the simplest ways is to use openFileOutput, which automatically writes to your internal files directory.
fun saveTextToFile(fileName: String, text: String) {
openFileOutput(fileName, MODE_PRIVATE).use { output ->
output.write(text.toByteArray())
}
}
This method creates the file if it does not exist, or replaces its contents if it does. The MODE_PRIVATE flag keeps the file private to your app.
You can also build a File manually if you want more control over paths.
val file = File(filesDir, "notes.txt")
file.writeText("Hello internal storage")When you use these APIs, you do not need any storage permissions. Internal storage access is always allowed for your own app.
Internal storage writes do not require the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permissions. If you are only using internal storage, do not request external storage permissions.
Reading Files from Internal Storage
Reading is similar to writing. You open an input stream and then read the content.
If you used openFileOutput, you can use openFileInput to read the same file.
fun readTextFromFile(fileName: String): String {
return openFileInput(fileName).bufferedReader().use { reader ->
reader.readText()
}
}
If you created a File object, you can use its read helpers.
val file = File(filesDir, "notes.txt")
val content = file.readText()When you read a file, be prepared for the possibility that it does not exist yet. In that case the read methods can throw an exception.
Listing and Managing Internal Files
Sometimes you need to know what files exist in your internal directory. You can use fileList if you saved files using openFileOutput, or you can use listFiles on the directory File.
val names = fileList()
for (name in names) {
println("Internal file: $name")
}Or using the directory:
val files = filesDir.listFiles()
files?.forEach { file ->
println("Path: ${file.absolutePath}")
}
To delete a file that you created with openFileOutput, you can call deleteFile.
val deleted = deleteFile("notes.txt")
If you created your file with File, you can remove it using:
val file = File(filesDir, "notes.txt")
val deleted = file.delete()Use deletion carefully. Once removed, the file data cannot be recovered by your app.
Working with Cache in Internal Storage
Your app can also store temporary data in the internal cache directory returned by cacheDir. This is useful for images, downloaded content, or anything that you can recreate.
val tempFile = File.createTempFile(
prefix = "image_",
suffix = ".tmp",
directory = cacheDir
)The system may delete cache files to free space. You should never store important user data in the cache directory.
You can clear your own cache files if you know they are no longer needed.
fun clearInternalCache() {
cacheDir.listFiles()?.forEach { file ->
file.delete()
}
}File Modes and Access Flags
When you use openFileOutput, you pass a mode flag. The most common one is MODE_PRIVATE. This mode makes sure that the file is only visible to your app and that any previous content is replaced.
There are other modes in the API, but many older flags are now discouraged. In modern Android development, you typically use MODE_PRIVATE only and manage file content yourself.
Always use MODE_PRIVATE for internal storage files. Do not rely on other modes for sharing files. Use proper sharing APIs instead of weakening your internal file privacy.
Internal Storage and User Privacy
Internal storage is a good place for data that must stay inside your app. Examples are user configuration, cached authentication tokens, and small databases.
Even though other apps cannot directly read these files, your app should still avoid storing secrets in plain text if exposure would be critical. Devices can be rooted or physically accessed, which can bypass normal app sandboxing.
When you design your storage layer, treat internal storage as private to your app, but not as a complete security boundary against very determined attackers.
Handling Errors and Edge Cases
File operations can fail. The internal storage might be full, a file might not exist, or there could be I/O errors. It is important to handle failures so that your app does not crash.
You can catch exceptions around file operations.
fun safeRead(fileName: String): String? {
return try {
openFileInput(fileName).bufferedReader().use { it.readText() }
} catch (e: FileNotFoundException) {
null
} catch (e: IOException) {
null
}
}
If you receive null, you can decide whether to show a default value, ask the user to try again, or recreate the file with default content.
Also consider the size of data. Very large files in internal storage may affect app performance and device storage. For large or long lived data, combine internal storage with other techniques like databases, which are covered in another chapter.
Choosing Internal Storage in Your App
When you decide where to store data, internal storage is often the first option you should consider. It requires no runtime permissions, it is easier to manage than external storage, and it respects user privacy.
Use internal storage for app specific files that only your app needs. Store them under filesDir for persistent data and under cacheDir for temporary data.
By following this pattern, you keep your data organized and your app simpler, while allowing the system to safely manage your files over the lifetime of your application.