Storage & SharedPreferences

Save settings, files, and app data locally on the device

SharedPreferences Internal Storage External Storage Persistence

Table of Contents

Storage Overview

Android offers several ways to persist data locally. Choose the right option based on data size, structure, privacy, and whether the user should access files outside your app.

App Data Storage Options ───────────────────────────────────────── SharedPreferences → Key-value settings (theme, login flag) Internal Storage → Private app files (notes, cache) External Storage → Larger/media files (scoped storage) Room / SQLite → Structured relational data (next lesson)
Storage TypeData FormatVisibilityBest For
SharedPreferencesKey-value pairsApp-privateSettings, flags, small strings
Internal StorageFiles, streamsApp-privateNotes, JSON cache, sensitive files
External StorageFiles, mediaApp-specific or shared (scoped)Photos, downloads, exports
Room DatabaseStructured tablesApp-privateContacts, todos, large datasets

1SharedPreferences CRUD

SharedPreferences stores primitive data (String, Int, Boolean, Float, Long) as key-value pairs. Data persists across app restarts and is private to your app.

Create — get SharedPreferences instance

Kotlin — Open preferences file// Default preferences file (packageName_preferences.xml) val prefs = getSharedPreferences("app_settings", Context.MODE_PRIVATE) // Or use the default preferences val defaultPrefs = PreferenceManager.getDefaultSharedPreferences(this)

Create / Update — write values

Kotlin — Save settings (Create + Update)fun saveUserSettings(username: String, darkMode: Boolean, fontSize: Int) { prefs.edit().apply { putString("username", username) putBoolean("dark_mode", darkMode) putInt("font_size", fontSize) putFloat("volume", 0.8f) putLong("last_login", System.currentTimeMillis()) apply() // Async — preferred on main thread // commit() returns Boolean — use when you need synchronous result } }

Read — retrieve values

Kotlin — Read preferences with defaultsval username = prefs.getString("username", "Guest") ?: "Guest" val darkMode = prefs.getBoolean("dark_mode", false) val fontSize = prefs.getInt("font_size", 16) val volume = prefs.getFloat("volume", 1.0f) val lastLogin = prefs.getLong("last_login", 0L) // Check if key exists val hasUsername = prefs.contains("username")

Delete — remove keys or clear all

Kotlin — Delete preferences// Remove single key prefs.edit().remove("username").apply() // Clear entire preferences file prefs.edit().clear().apply()

Listen for changes

Kotlin — OnSharedPreferenceChangeListenerprivate val prefListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPrefs, key -> when (key) { "dark_mode" -> { val enabled = sharedPrefs.getBoolean(key, false) applyTheme(if (enabled) Theme.DARK else Theme.LIGHT) } } } override fun onResume() { super.onResume() prefs.registerOnSharedPreferenceChangeListener(prefListener) } override fun onPause() { prefs.unregisterOnSharedPreferenceChangeListener(prefListener) super.onPause() }

Tip: Use apply() for async writes on the UI thread. Use commit() only when you need a synchronous boolean result. Never store passwords or tokens in plain SharedPreferences — use EncryptedSharedPreferences instead.

2Internal Storage

Internal storage is private to your app. Files are deleted when the app is uninstalled. Other apps cannot access them without root.

Internal storage locations

Kotlin — Internal directories// Files directory — persistent app files val filesDir = filesDir // /data/data/com.example.app/files/ val file = File(filesDir, "notes.txt") // Cache directory — can be cleared by system when low on space val cacheDir = cacheDir // /data/data/com.example.app/cache/ // Custom subdirectory val notesDir = File(filesDir, "notes") if (!notesDir.exists()) notesDir.mkdirs()

Write text file to internal storage

Kotlin — Save note internallyfun saveNoteInternal(filename: String, content: String) { openFileOutput(filename, Context.MODE_PRIVATE).use { outputStream -> outputStream.write(content.toByteArray()) } } fun readNoteInternal(filename: String): String { return openFileInput(filename).use { inputStream -> inputStream.bufferedReader().readText() } }

List and delete internal files

Kotlin — Manage internal files// List all files in filesDir val fileList = filesDir.listFiles()?.map { it.name } ?: emptyList() // Delete a file deleteFile("notes.txt") // Or using File API File(filesDir, "notes.txt").delete()

3External Storage

External storage (shared storage) holds larger files like photos and downloads. Modern Android uses scoped storage — apps access app-specific directories without broad permissions, and need permissions or MediaStore for shared files.

Manifest permissions (when needed)

XML — AndroidManifest.xml (legacy / specific cases)<!-- Android 12 and below for broad file access --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <!-- Android 13+ for media types --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

App-specific external directory (no permission needed)

Kotlin — getExternalFilesDir()// App-specific external folder — removed on uninstall val externalDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES) // /storage/emulated/0/Android/data/com.example.app/files/Pictures/ val photoFile = File(externalDir, "profile_${System.currentTimeMillis()}.jpg") // Cache on external storage val externalCache = externalCacheDir

Check storage state

Kotlin — External storage availabilityfun isExternalStorageWritable(): Boolean { return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED } fun isExternalStorageReadable(): Boolean { val state = Environment.getExternalStorageState() return state == Environment.MEDIA_MOUNTED || state == Environment.MEDIA_MOUNTED_READ_ONLY }

Scoped storage summary

LocationPermission RequiredRemoved on Uninstall
getExternalFilesDir()NoYes
getExternalCacheDir()NoYes
MediaStore (Photos, Downloads)READ_MEDIA_* or pickerNo (user files)
Storage Access Framework (SAF)User picks file via pickerDepends on file

4Reading/Writing Files

Beyond SharedPreferences, apps read and write text, JSON, and binary files using Kotlin I/O APIs. Always use coroutines or background threads for large file operations.

Write and read text with File API

Kotlin — File read/writefun writeTextFile(file: File, content: String) { file.writeText(content) } fun readTextFile(file: File): String { return if (file.exists()) file.readText() else "" } fun appendToFile(file: File, line: String) { file.appendText("$line\n") }

Save and load JSON with Gson

Kotlin — JSON file persistencedata class UserProfile(val name: String, val email: String, val score: Int) fun saveProfileToJson(profile: UserProfile) { val json = Gson().toJson(profile) File(filesDir, "profile.json").writeText(json) } fun loadProfileFromJson(): UserProfile? { val file = File(filesDir, "profile.json") if (!file.exists()) return null return Gson().fromJson(file.readText(), UserProfile::class.java) }

Copy file with streams

Kotlin — Copy file with bufferfun copyFile(source: File, destination: File) { source.inputStream().use { input -> destination.outputStream().use { output -> input.copyTo(output) } } }

Background I/O with coroutines

Kotlin — IO dispatcher for file worklifecycleScope.launch { val content = withContext(Dispatchers.IO) { readTextFile(File(filesDir, "notes.txt")) } binding.tvNotes.text = content } fun saveNotesAsync(notes: String) { lifecycleScope.launch(Dispatchers.IO) { writeTextFile(File(filesDir, "notes.txt"), notes) } }

Pick file with Storage Access Framework

Kotlin — Open document pickerval pickFileLauncher = registerForActivityResult( ActivityResultContracts.OpenDocument() ) { uri: Uri? -> uri?.let { contentResolver.openInputStream(it)?.use { stream -> val text = stream.bufferedReader().readText() binding.tvImported.text = text } } } // Launch picker binding.btnImport.setOnClickListener { pickFileLauncher.launch(arrayOf("text/plain", "application/json")) }

5Data Persistence

Data persistence means your app retains data across process death, configuration changes, and app restarts. Choose the right layer for each type of data.

Persistence strategy guide

Data TypeSolutionExample
User preferences / togglesSharedPreferencesDark mode, language, onboarding done
Small JSON / text documentsInternal fileDraft notes, cached API response
Images / large mediaExternal app-specific dirCaptured photos, exported PDFs
Structured lists / relationsRoom databaseContacts, todos, chat messages
Sensitive secretsEncryptedSharedPreferences / KeystoreAuth tokens, API keys
UI state during rotationViewModel + SavedStateHandleForm input, scroll position

Repository pattern for persistence

Kotlin — SettingsRepository.ktclass SettingsRepository(context: Context) { private val prefs = context.getSharedPreferences("app_settings", Context.MODE_PRIVATE) private val gson = Gson() private val profileFile = File(context.filesDir, "profile.json") var darkMode: Boolean get() = prefs.getBoolean("dark_mode", false) set(value) = prefs.edit().putBoolean("dark_mode", value).apply() var username: String get() = prefs.getString("username", "") ?: "" set(value) = prefs.edit().putString("username", value).apply() fun saveProfile(profile: UserProfile) { profileFile.writeText(gson.toJson(profile)) } fun loadProfile(): UserProfile? { if (!profileFile.exists()) return null return gson.fromJson(profileFile.readText(), UserProfile::class.java) } fun clearAllData() { prefs.edit().clear().apply() profileFile.delete() } }

EncryptedSharedPreferences for sensitive data

Kotlin — Encrypted preferences// dependencies { implementation("androidx.security:security-crypto:1.1.0-alpha06") } val masterKey = MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build() val encryptedPrefs = EncryptedSharedPreferences.create( context, "secure_prefs", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) encryptedPrefs.edit().putString("auth_token", token).apply()

What survives what?

Screen rotation → ViewModel / onSaveInstanceState Process killed → SharedPreferences, files, Room DB persist App update → Internal data persists (unless you change storage keys) App uninstall → All app data deleted (internal + app-specific external) Clear app data → User wipes SharedPreferences, files, database

6Hands-On Exercises

1

Build a settings screen that saves username, dark mode toggle, and font size to SharedPreferences. Load values on app start and apply them.

2

Implement full CRUD on SharedPreferences: save, read, update one key, delete one key, and clear all.

3

Create a notes app that saves text to internal storage using openFileOutput. List all saved note filenames.

4

Save a captured photo to app-specific external storage with getExternalFilesDir(DIRECTORY_PICTURES).

5

Serialize a UserProfile object to JSON and read/write it from a file in filesDir.

6

Wrap all storage logic in a Repository class. Use coroutines with Dispatchers.IO for file operations.

7

Bonus: Use the Storage Access Framework to let the user import a text file and display its contents in the app.

Storage & SharedPreferences MCQ Practice

10 Basic MCQs 10 Advanced MCQs

10 Basic Storage & SharedPreferences MCQs

1

SharedPreferences stores?

ARelational tables
BKey-value pairs persistently
CVideo streams
DDex bytecode
Explanation: Lightweight prefs: settings, flags, small strings.
2

Internal storage files are?

AOn SD card only
BWorld readable always
CPrivate to your app by default
DShared with all apps
Explanation: Other apps cannot access internal storage without root.
3

getSharedPreferences(name, MODE_PRIVATE)?

ARequires internet
BPublic world write
CDeletes all data
DOpens named prefs file private to app
Explanation: MODE_PRIVATE is standard; avoid deprecated world-readable modes.
4

Commit vs apply on Editor?

Aapply() async; commit() sync returns boolean
BSame
Capply blocks UI always
Dcommit illegal
Explanation: apply() preferred on main thread; commit() for critical immediate persist.
5

External storage on modern Android?

AAlways free access
BRequires scoped storage and permissions
CSame as internal always
DDeprecated entirely
Explanation: Use MediaStore or SAF for shared external files.
6

openFileOutput creates file in?

ASystem partition
BOther app's folder
CApp internal storage directory
DPlay Console
Explanation: context.openFileOutput(filename, MODE_PRIVATE) writes internally.
7

SharedPreferences best for?

AVideo editing
BLarge image blobs
CFull contact database
DUser settings and small config
Explanation: Not for large data — use files, Room, or DataStore.
8

read text file internally?

AopenFileInput + BufferedReader
BIntent filter
CBroadcastReceiver
DGradle task
Explanation: Stream API or readText extension on File in filesDir.
9

filesDir points to?

ASD card root
BInternal files directory for app
CCache always
DManifest path
Explanation: context.filesDir — persistent internal storage.
10

cacheDir used for?

ASigned APK
BPermanent user documents
CTemporary files system may delete
DManifest merge
Explanation: Cache cleared when low storage or user clears cache.

10 Advanced Storage & SharedPreferences MCQs

11

DataStore replaces SharedPreferences for?

ASQL only
BType-safe async preferences with Flow
CCamera capture
DNotifications only
Explanation: Jetpack DataStore (Preferences/Room) is modern replacement.
12

Scoped storage (Android 10+) means?

AAll files internal
BNo external storage
CApps use app-specific dirs and MediaStore for shared media
DRoot required
Explanation: Legacy file paths restricted; use proper APIs.
13

MANAGE_EXTERNAL_STORAGE?

AAutomatic grant
BDefault for all apps
CReplaces READ_MEDIA
DSpecial broad access — rarely approved on Play Store
Explanation: Highly restricted; use granular media permissions instead.
14

EncryptedSharedPreferences?

AAES encryption for sensitive prefs via Jetpack Security
BGoogle Drive sync
CRoom replacement
DNetwork protocol
Explanation: androidx.security.crypto.EncryptedSharedPreferences for tokens.
15

SAF (Storage Access Framework)?

ADirect path to /sdcard always
BUser picks files/dirs via system picker
CADB only
DKernel API
Explanation: ACTION_OPEN_DOCUMENT for persistent URI access.
16

Backup SharedPreferences?

AImpossible
BAutomatic cloud always
Candroid:allowBackup and backup rules XML
DRequires Firebase only
Explanation: Configure backup_rules.xml to include/exclude prefs.
17

Multi-process SharedPreferences?

ARequired for Services
BRecommended default
CFaster than Room
DDeprecated/unreliable — use ContentProvider or DataStore
Explanation: MODE_MULTI_PROCESS removed — don't share prefs across processes.
18

Large JSON config storage?

AFile in internal storage or assets
BSharedPreferences
CToast
DLogcat
Explanation: Prefs not sized for large JSON — use files or Room.
19

getExternalFilesDir(null)?

AGlobal SD root
BApp-specific external directory no permission needed
CInternal only
DSystem app dir
Explanation: Removed on uninstall; good for large app-private external files.
20

clear() on SharedPreferences Editor?

AClears cache only
BUninstalls app
CRemoves all keys in that prefs file
DRevokes permissions
Explanation: editor.clear().apply() wipes the named preferences file.
Click an option to select, then check answers.

Storage & SharedPreferences Interview Q&A

15 topic-focused questions for interviews and revision.

1When to use SharedPreferences?easy
Answer: Small key-value data: theme, login flag, last username, feature toggles — not large or structured data.
2Save and read a boolean preference.easy
Answer: prefs.edit().putBoolean("dark_mode", true).apply(); val dark = prefs.getBoolean("dark_mode", false)
3Internal vs external storage.easy
Answer: Internal: private, always available. External: may be removable, needs permissions/scoped APIs for shared access.
4apply() vs commit().medium
Answer: apply() writes asynchronously without blocking UI; commit() synchronous and returns success — use for must-save-before-exit.
5Write string to internal file.easy
Answer: openFileOutput("notes.txt", MODE_PRIVATE).use { it.write(text.toByteArray()) }
6Why not store passwords in plain SharedPreferences?hard
Answer: Readable on rooted devices and backups — use EncryptedSharedPreferences or Android Keystore.
7Scoped storage impact on file apps.medium
Answer: Must use SAF or MediaStore; direct path access limited for other apps' files.
8DataStore advantages over SharedPreferences.medium
Answer: Coroutine/Flow support, type safety, transactional consistency, handles migration better.
9Where are SharedPreferences files stored?medium
Answer: /data/data/package/shared_prefs/name.xml on internal storage.
10Clear user settings on logout.easy
Answer: prefs.edit().clear().apply() or remove specific keys.
11Read file from assets.medium
Answer: context.assets.open("config.json").bufferedReader().use { it.readText() }
12cacheDir vs filesDir.easy
Answer: filesDir persistent app data; cacheDir disposable temp files OS may clear.
13Request READ_MEDIA_IMAGES permission.medium
Answer: Android 13+ granular media permission for accessing images — declare and request at runtime.
14Persist login token securely.hard
Answer: EncryptedSharedPreferences or Encrypted DataStore; never plain text prefs.
15Backup considerations for prefs.hard
Answer: Exclude sensitive keys in backup_rules.xml; tokens should not auto-restore to wrong device unencrypted.