Android Storage & Scoped Storage
Part-1 File storage basics
Overview
Android is bringing in lot of improvements in accessing the device storage for Read/Write operations. It’s all started when it introduced runtime Permissions, where an app must ask for user’s permissions for accessing any kind of shared storage data in the device. From then on android imposes lot of strict measures in accessing the data from the device only if the app really needs those data for performing its work.
Storage in android system is classified as follows:
- App-specific storage
- Shared storage
- Preferences
- Database
To understand the reason for introducing scoped storage in android, we need to have some basic understanding of how media files and documents are accessed, edited or stored in the internal or external storage by an app and the permissions provided to the app for accessing the shared files in the device.
Application Storage
App storage can be classified into:
- Internal Storage(Files saved here are protected and accessible only to the app which created/stored it)
- External Storage (Sometimes this is a part of android device storage, SD cards, etc.,)
Files which are stored in internal storage cannot be accessed by the other applications. Only the app which created those files can access it. This provides one form of security for an app to store its sensitive files.
Files that were stored in external storage are accessible to other apps as well provided those apps have declared proper storage permissions. Although it’s possible for other apps to access those files, the files stored in these directories are meant for use only by the particular app.
If an app wants to stores files in external storage where others apps should access those files for their operation then those files should be stored in the shared storage. One example for this particular usecase is Camera application. Files created/edited by the camera app should be accessible to Gallery app, File manager app, etc.,
App-specific directories in Internal Storage
Android OS provides considerable amount of space in internal storage for the app to access its directories. Usually these directories can be called from a context object,
- App persistent directory
val file = File(context.filesDir, filename)
2. App cache directory
val cacheFile = File(context.cacheDir, filename)
App-specific directories in External Storage
If internal storage doesn’t provide enough space to store app-specific files, android provides external storage for an app to access its directories. Like internal storage app specific directories, these directories can also be called from a context object,
- App persistent directory
val file = File(context.filesDir, filename)
2. App cache directory
val cacheFile = File(context.cacheDir, filename)
Choosing Storage location
Sometimes, a device that allocates a partition of its internal memory as an external storage might also provides an SD card slot. This makes the devices to have a provision for holding multiple storage volumes. Hence to access different storage volumes, we have to call this particular function,
val externalStorageVolumes: Array<out File> =
ContextCompat.getExternalFilesDirs(applicationContext, null)
val primaryExternalStorage = externalStorageVolumes[0]
As shown in the code snippet, the first element in the returned array is considered as the primary external storage volume allocated by the device.
Storage Availability
Most often external storage may also refers to the externally mounted disk(SD card). Hence it is important to verify that particular volume is accessible or not before trying to read app-specific data from, or write app-specific data to, external storage.
// Checks if a volume containing external storage is available
// for read and write.
fun isExternalStorageWritable(): Boolean {
return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}
// Checks if a volume containing external storage is available to at least read.
fun isExternalStorageReadable(): Boolean {
return Environment.getExternalStorageState() in
setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY)
}
Query Free space
Many times devices don’t have much storage space available henceforth the app should consume space thoughtfully. We can query the free space availability using the below snippet and perfore storage operations,
// App needs 10 MB within internal storage.
const val NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L;
val storageManager = applicationContext.getSystemService<StorageManager>()!!
val appSpecificInternalDirUuid: UUID = storageManager.getUuidForPath(filesDir)
val availableBytes: Long =
storageManager.getAllocatableBytes(appSpecificInternalDirUuid)
if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) {
storageManager.allocateBytes(
appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP)
} else {
val storageIntent = Intent().apply {
action = ACTION_MANAGE_STORAGE
}
// Display prompt to user, requesting that they choose files to remove.
}
In the Part 2 of this series, let’s build an app and experiment all the topics we have covered. We also look into File Provider API and experiment it.
Cheers……🍺🍺🍺