Welcome to the first part of a two-part series on working with file storage in Android! A vast majority of apps are doing some form of data management. Whether it’s just loading a profile image, sharing images or video/audio files through messaging or simply storing data. Working with files on Android can be daunting. Especially if you’re new to Android, or just haven’t worked with it in a while.
When it comes to saving data on Android, we can choose from a few options:
If you are wondering which of these options is for you, there are four simple questions in the official documentation that you can go through to figure out what type of storage you need.
In this article though, I am going to focus on storing data to a disk using some of the APIs provided by Android.
Topics we are going to cover include:
Android system provides us with two types of physical storage locations: internal and external storage.
The most significant difference between them is that the internal storage is smaller than external storage on most devices. Also, the external storage might not always be available, in contrast to internal, which is always available on all devices. The system creates an internal storage directory and names it with the app’s package name.
Keeping in mind that the internal storage is always available, this makes it a reliable place to store data that your app depends on. Furthermore, if you need to save sensitive data or data that only your app can have access to, go for the internal storage.
Two things are important to note when working with internal storage:
To access and store data to app’s files in the internal storage, you can use File API. I wrote a simple method just for an example:
Alternatively, you can call openFileOutput() method to obtain an instance of FileOutputStream that can write to a file in the filesDir directory.
You would read from a file in this directory in almost the same way. You can create a File and call readText() method or you can obtain an instance of FileInputStream by calling openFileInput().
Here’s an example:
Well, as I mentioned earlier, external storage is usually larger than internal storage. So, the first thing that comes to mind is to use it for storing larger files or apps. And that exactly is the most common usage of the external storage.
Since internal storage has limited space for app-specific data, it’s generally a good idea to use external storage for all of the non-sensitive data your app works with. Even if they are not so large.
Generally, data you save to this storage is persistent through app uninstallation. There is a case though, where files are going to be removed when an app is deleted. If you store app-specific files to the external storage by using getExternalFilesDir(), you will lose the files when the app is uninstalled, so be aware of that. Additionally, data stored in this directory can be accessed by other applications if they have appropriate permission.
When it comes to working with external storage, there are few things you should do before storing data there:
Android defines two permissions related to storage: READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE. As you can see, permissions are only defined for accessing external storage. That means that every app, by default, has permissions to access its internal storage.
On the other hand, if your app has to access external storage, you are obliged to request permission for that. That means if you’re trying to access media on external storage by using MediaStore API you will need to request READ_EXTERNAL_STORAGE permission.
However, few exceptions to this rule exist:
You also don’t need any permissions if you’re trying to obtain any documents or other types of content when using Storage Access Framework. That’s because a user is involved in the process of selecting the actual content to work with.
When defining permissions, you can set a condition to only apply it for some versions. For example:
In the example above I defined WRITE_EXTERNAL_STORAGE permission only for version 28 and below. This can come in handy in situations where apps running newer versions can work without permission, but apps on older versions will break without it.
Android is focusing on privacy more and more with every release. Application Sandboxing is a core part of Android’s design, isolating apps from each other. Following that principle, the Android team introduced Scoped storage in Android 10. As the team said, “It’s a way to ensure transparency, give users control and secure personal data.”
From the official documentation:
More recent versions of Android rely more on a file’s purpose than its location for determining an app’s ability to access that file. This purpose-based storage model improves user privacy because apps are given access only to the areas of the device’s file system that they use.
Before Android 10, apps have private, app-specific storage. In addition to private storage, the system provides shared storage where all of the other files are stored. The problem is that apps can access all of these files when given storage permission. From a user’s perspective, this doesn’t make sense.
Let’s take an example with an app that needs to provide users with an ability to select a profile image. To allow the app to access images you need to ask for storage reading permission. Given the permission, your app can now access images but also all the other files within the shared storage. There is no need for your app to be able to see every file, only to allow the user to select an image. This is one of the main reasons scoped storage has been introduced.
This means that apps that target Android 10 and higher are given scoped access to external storage, or scoped storage, by default. With this change, apps have unrestricted access to app-specific storage or media that the app itself has created. But, to read other applications’ media files, you need to request reading permission.
For accessing MediaStore.Downloads collection files that your app didn't create, you must use Storage Access Framework.
If your app is not ready to adopt scoped storage yet, you have two options to opt-out of using it:
You can read more about Android 11 storage updates here.
Make sure to check the second part of this post! 😊