Instruction
In this lesson, you’ll learn how to store data in files. You’ll learn about internal and external storage. You’ll also learn about permissions and shared and scoped storage.
Reading and Writing Files
Android uses a file system like other disk-based file systems. The system provides the following options for you to save your app data:
- App-specific storage: Stores files that are meant for your app’s use only. You can store them in dedicated directories within internal storage or external storage. App-specific storage is best for user data that only your app can access.
- Shared storage: Stores files that your app intends to share with other apps. Files to be stored in shared storage include media files, documents, and other files that the user expects to be accessible to other apps.
- Preferences: Stores key-value pairs of primitive data types in a file within internal storage. Preferences are best for storing small amounts of data that aren’t sensitive. You’ve already learned about DataStore, which is a modern way to store key-value pairs.
- Databases: Stores structured data in a private database within internal storage. An example of this is the Room persistence library.
App-Specific Storage
App-specific storage can be internal or external. Internal storage is always available to your app, and it’s always accessible. External storage isn’t always available, and it may be removed by the user or other apps. Always use internal storage when you don’t want the user of your app or other apps to access the files.
Internal Storage
Internal storage is private to your app. The Android system creates a directory for each app. The directory is named after the app’s package name. When a user uninstalls your app, the system deletes all the files in the internal storage directory.
To access the internal storage directories, you use the context.getFilesDir() function. This function returns a File object representing the internal storage directory. You can then use this object to create files and directories in the internal storage directory.
External Storage
External storage can either be a dedicated partition on the devices or a removable storage medium. This means that external storage isn’t always available. For example, a user can mount a removable SD card on their phone and remove it. If you want to use external storage, you must check the available external storage directories and if the files exist before using them. To access the external storage directories, you use the context.getExternalFilesDir() function. This function returns a File object representing the external storage directory. You can then use this object to create files and directories in the external storage directory.
For your app to use external storage, you must request some permissions. You’ll learn about these permissions next.
Permissions
To declare permissions, you must add the <uses-permissions> element in your app’s AndroidManifest.xml file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.kodeco.android.devscribe">
<uses-permission .../>
...
</manifest>
To use external storage, you need to declare the correct EXTERNAL_STORAGE permission. If you only need to read external files, you need the READ_EXTERNAL_STORAGE permission, which can be declared as follows:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
If you need to write to external storage, you need the WRITE_EXTERNAL_STORAGE permission, which can be declared as follows:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
If your app needs to read and write to external storage, you need to declare both permissions.
Declaring permissions in the manifest file isn’t enough to have them granted to you. From Android 6.0 (API level 23), you need to request permissions at runtime. Giving access to files can compromise the user’s privacy. You need consent from the user before you can use permissions and get access to their files.
Any time a file request or access is made Android will check to see if the permission is given. Permission aren’t granted once and left in that state. A user can revoke permissions at any time. With newer Android versions, the system will check for permissions that haven’t been used in a long time. These permissions can be automatically revoked.
Also, a user can allow the permissions only when the app is in use or for a single use of the app. To prevent crashes or unexpected behavior, you must always check for permissions. If the external storage operation isn’t allowed, you’ll need to gracefully handle that condition. So, if the user denies the permission, inform the user why you need the permission.
One point to note is that on older Android versions, you need special permission to read and write files outside your app’s own folder. Newer Android versions care more about what the file is for, not where it is. Also, permissions are different for apps made for Android 11 (API level 30) or higher. The WRITE_EXTERNAL_STORAGE permission doesn’t matter for storage access. Instead, use the MANAGE_EXTERNAL_STORAGE permission to handle files outside your app’s directory.
Using Shared Storage and Scoped Storage
Shared storage is for data that should be accessible to other apps even when your app is uninstalled. Shared storage includes media files, documents, and other files that the user expects to be accessible to other apps. Android also provides APIs to store and access the following types of files in shared storage:
- Media files: There’s a standard public directory for this kind of file. The user has this common location for all their photos, another common location for audio files, and so on. If you want to access media files, you can use the MediaStore API.
- Documents: There’s a special directory that has other file types like PDFs, text files, or books that use the EPUB format. If you want to access documents, you can use the Storage Access Framework.
- Datasets: For Android 11 (API level 30) and higher, the system caches large datasets. These can be utilized by many applications. Cached large datasets support use cases such as machine learning and media playback. Applications can access these shared datasets via the BlobStoreManager API.
Before Android 10 (API level 29), apps could access any file on the device’s external storage. It’s kind of like the Wild West of storage systems. :]
Regardless of who created the files or where they were stored, anyone could access them. This was a security and privacy risk because apps could access sensitive data from other apps. So, there was no difference between extra storage and shared storage. To mitigate these risks, Android introduced Scoped Storage starting with Android 10. Scoped storage is a way for apps to store and access files in a way that’s isolated from other apps. With scoped storage, apps can only access their files and directories or files that are in the public Downloads directory. This helps to protect user’s data and privacy. Scoped Storage also aids file organization. Android enforces Scoped Storage even when an app has READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions.
Apps targeting Android 11 (API level 30) or higher must use scoped storage. If your app targets Android 10 (API level 29) or lower, you can still use the legacy storage model. But, you should consider migrating to scoped storage to improve the security of your app and better protect user data.