Junior
How is data stored in Android?
sobes.tech AI
Answer from AI
In Android, various mechanisms are used for data storage, depending on the type of data, their size, security requirements, and accessibility.
Main data storage methods:
-
SharedPreferences:
- For small amounts of primitive data (boolean, int, float, long, String, Set<String>).
- Stored in XML files.
- Not intended for concurrent access from multiple processes.
// Getting SharedPreferences val sharedPref = context.getSharedPreferences("my_prefs", Context.MODE_PRIVATE) // Saving data with (sharedPref.edit()) { putBoolean("is_logged_in", true) putString("username", "user123") apply() // Asynchronous save // commit() // Synchronous save } // Reading data val isLoggedIn = sharedPref.getBoolean("is_logged_in", false) val username = sharedPref.getString("username", null) -
Internal Storage:
- For storing private application files.
- Files are accessible only to the app itself.
- Automatically deleted when the app is uninstalled.
// Writing to a file val filename = "my_data.txt" val fileContents = "Some data to write." context.openFileOutput(filename, Context.MODE_PRIVATE).use { it.write(fileContents.toByteArray()) } // Reading from a file context.openFileInput(filename).bufferedReader().useLines { lines -> lines.forEach { // Process each line } } -
External Storage:
- For storing publicly accessible data (photos, videos, documents).
- Files can be accessed by other apps and the user via a file manager.
- May be absent or not mounted, so its availability should be checked.
- Starting from Android 10, Scoped Storage is used to restrict access to other apps' files.
// Checking external storage availability fun isExternalStorageWritable(): Boolean { return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED } fun isExternalStorageReadable(): Boolean { return Environment.getExternalStorageState() in setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY) } // Getting directory for saving files (e.g., documents) val directory = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) val file = File(directory, "my_external_file.txt") // Writing to a file on external storage FileWriter(file).use { writer -> writer.write("Data on external storage.") } -
SQLite Databases:
- For storing structured data in a relational format.
- Supports SQL queries.
- Used for more complex data storage scenarios.
- Libraries like Room Persistence Library simplify database operations.
Example using Room:
// Entity definition @Entity data class User( @PrimaryKey val uid: Int, @ColumnInfo(name = "first_name") val firstName: String?, @ColumnInfo(name = "last_name") val lastName: String? ) // DAO (Data Access Object) definition @Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): List<User> @Query("SELECT * FROM user WHERE uid IN (:userIds)") fun loadAllByIds(userIds: IntArray): List<User> @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") fun findByName(first: String, last: String): User @Insert fun insertAll(vararg users: User) @Delete fun delete(user: User) } // Room database definition @Database(entities = [User::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao } // Using the database val db = Room.databaseBuilder( applicationContext, AppDatabase::class.java, "database-name" ).build() val userDao = db.userDao() val users: List<User> = userDao.getAll() -
Content Providers:
- Allow apps to share data with other apps.
- Used to access system data (contacts, media) or to provide data to other apps.
- Require defining a URI (Uniform Resource Identifier) for data identification.
// Example of querying contacts using ContentResolver val cursor: Cursor? = contentResolver.query( ContactsContract.Contacts.CONTENT_URI, null, // Projection (columns to return) null, // Selection criteria (WHERE clause) null, // Selection arguments null // Sort order ) cursor?.use { while (it.moveToNext()) { val contactId = it.getLong(it.getColumnIndexOrThrow(ContactsContract.Contacts._ID)) // Access other contact data } } -
DataStore:
- A more modern and flexible alternative to SharedPreferences.
- Uses Kotlin Coroutines and Flow for asynchronous data operations.
- Available in two implementations: Preferences DataStore (for primitive data) and Proto DataStore (for objects using Protocol Buffers).
Example using Preferences DataStore:
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings") // Saving data (within a CoroutineScope) suspend fun saveSetting(key: Preferences.Key<Boolean>, value: Boolean) { context.dataStore.edit { settings -> settings[key] = value } } // Reading data (within a CoroutineScope) val exampleSetting: Flow<Boolean> = context.dataStore.data .map { preferences -> preferences[booleanPreferencesKey("example_setting")] ?: false }
The choice of the optimal data storage method depends on your application's specific requirements.